-
Notifications
You must be signed in to change notification settings - Fork 446
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
A simple way to implement resubmit/recirculate/clone3 in P4-16 on v1model #1698
Conversation
backends/bmv2/common/extern.cpp
Outdated
@@ -117,117 +117,6 @@ ExternConverter::convertExternFunction(ConversionContext* ctxt, | |||
return primitive; | |||
} | |||
|
|||
void | |||
ExternConverter::modelError(const char* format, const IR::Node* node) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have moved these 3 methods to the Context class, where they make more sense.
@@ -82,6 +82,119 @@ unsigned nextId(cstring group) { | |||
return counters[group]++; | |||
} | |||
|
|||
} // namespace BMV2 | |||
void | |||
ConversionContext::addToFieldList(const IR::Expression* expr, Util::JsonArray* fl) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these methods have been moved from extern.cpp and slightly simplified.
@@ -109,22 +109,22 @@ ExternConverter_action_profile ExternConverter_action_profile::singleton; | |||
ExternConverter_action_selector ExternConverter_action_selector::singleton; | |||
|
|||
Util::IJson* ExternConverter_clone::convertExternFunction( | |||
UNUSED ConversionContext* ctxt, UNUSED const P4::ExternFunction* ef, | |||
UNUSED const IR::MethodCallExpression* mc, UNUSED const IR::StatOrDecl* s, | |||
ConversionContext* ctxt, UNUSED const P4::ExternFunction* ef, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
many of these parameters are in fact used; the UNUSED annotation was wrong.
I do not have enough experience with the compiler internals to make useful comments on the code changes proposed, so I will focus on questions on the approach of using annotations to specify what metadata should be recirculated/resubmitted/cloned. If this annotation idea goes forward, it seems to have a significant limitation in how precise one can be in what you want to preserve, when there are nested structs. For example, if you have user-defined metadata where the "top level" struct is user_meta_t, with sub-structs like this:
Suppose I want to preserve mid1.x.b, but I do not need to preserve any of these:
If I put the @recirculate annotation on field b, does it preserve all 4 of those? Unless I am missing a way to specify this via annotations, it seems like it restricts you to preserving a field everywhere that the containing struct type occurs, even if that is in many places. |
Mihai, regarding this comment of yours: "I don't really understand what it means to recirculate standard_metadata, since it is supposed to reflect the state of the packet as received." standard_metadata as defined in P4_14 and v1model is just as much about what fields are outputs of ingress, and outputs of egress, that control what happens to the packet next. They are not only inputs. I do not know how common it is to do such a thing, but you could have a P4 program where the ingress control assigns a value to standard_metadata.egress_spec, which controls which output port the packet will go to after it finishes ingress (or whether it will be dropped, by storing a special invalid port number in that field). Maybe you want to do a resubmit to start ingress over again, and preserve the value of egress_spec calculated during the first pass. Yes, I understand that it is certainly possible to copy that value into a user-defined metadata field and preserve that instead. The reason I raise the issue is not that there isn't another way, but wondering aloud how common it may be that an existing collection of P4_14 programs might already do that, and therefore it might be important for practical use cases to preserve what seems to be existing behavior there. Answering this question would best be done with feedback from others with access to larger collections of P4_14 code than I do. |
As far as these nested structs are used inside a P4 header, then the midend FlattenHeaders pass will flatten the struct and also preserve any annotation for any struct. The flattened fields can be annotated too but not so right now because the bmv2 backend/behavioral model loses its mind on receiving each flattened field with an annotation. Likewise if the structs are use in instantiated controls, parsers, and packages, the struct will be flattened by the FlattenInterfaceStructs midend pass. You can use the p4test app and see that both midend passes will be active and flatten data. I hope this helps. |
You are right about preserving b everywhere. The only solution is to define two structs with the same layout one of which has annotations and one which doesn't. What bmv2 does with the standard_metadata when recirculating is one thing - you can really do anything in a software switch, the question which I find more important is whether real routers allow this kind of recirculated information (where much of the intrinsic metadata is under user control), and whether it is indeed such an important feature that we have to make it easy to do. |
Psa cannot recirculate standard metadata |
It isn't just two structs, necessarily. The structure of struct definitions can be an arbitrary DAG, and if you want only one field of a leaf, you would need to define copies of all structs from the root to the leaf. It also seems like an odd, workaround kind of thing to suggest that a developer do. |
@jafingerhut there are two questions to solve
The current PR is simple, but has some limitations. The one you point out is probably the most significant. The question is whether it is indeed significant in practice to cause a major inconvenience. In general the P4 programs I have seen have shallow structures. It may be also that the metadata that people want to recirculate is really application-specific, so it may not reside in structs that are reused in many places. If you want full flexibility (including recirculating standard metadata) the only solution in P4-16 that I can imagine is a pair of functions send/receive. Also, I consider v1model a legacy architecture (although it's the only one we really have at this point...), I think we should focus making PSA as nice as possible, even if this means some inconvenience for people who program against v1model. (But we should only allow minor inconveniences, we cannot afford to lose important features. It is my mistake that resubmit & co. do not work as expected, I should have realized that sooner. I hope that this patch is an example of an inconvenience.) |
Mihai, regarding this comment: "Psa cannot recirculate standard metadata", I guess the way I think of it is that PSA can recirculate any collection of bits you want to, and the source of these bits can be any state of the packet available, including standard metadata values, packet header field values, and/or user-defined metadata field values. You just copy the bits from wherever they are to the bits that are recirculated, then copy them from what was recirculated to wherever you want after recirculation. After recirculation, P4_16 does not let you assign them to the values of |
Yes, if you are happy with this limitation for v1model then we're good. |
Before I mentioned a concern on the efficiency that a compiler can turn a P4 program using these annotations for which fields to preserve, or not, when there are multiple resubmit, recirculate, or clone operations in the same program. Now I want to focus on the issue of whether translating a P4_14 program to a P4_16 program that uses a single set of preserved fields for all such operations could introduce a bug in the translated program. The P4_14 version of switch.p4 in this directory, for example: https://github.com/p4lang/p4c/tree/master/testdata/p4_14_samples/switch_20160512 The cloned packet, as it starts its egress processing, for each of these 5 different field lists, will in general have different values for the fields that are not preserved, vs. the ones that are preserved. The ones that are not preserved could be always 0 on some implementations, and perhaps the P4_14 language spec even mandates that, even though I suspect some implementations may let you weaken that to say that they have some unknown uninitialized values if they are not explicitly preserved. At least for implementations that guarantee "if you do not preserve this metadata field value, it will be 0 at the beginning of egress", a P4 program author could have relied on a non-preserved field being 0. If an implementation took the union of all such field lists every time a clone_ingress_pkt_to_egress operation was invoked, then the program's behavior has been changed, perhaps introducing a bug. I do not know if that switch.p4 program exhibits this property, because it is large enough that it is not very quick to check. It seems worth raising this issue for this kind of proposed implementation, at least, and also documenting it, if we choose to adopt this kind of translation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P4-14 spec was written with certain architecture in mind. The field_list was used as a reference to a set of fields which are recirculated at the end of ingress/egress processing (here I use recirculate
as a synonym for resubmit
, recirculate
and clone
). The implementation of the recirculation mechanism is bandwidth-constrained. As a result, it is important to minimize the amount of the extra metadata to be recirculated. Hence, the idea of field list was created to allow programmer to specify the exact set of fields to be recirculated for a given packet.
With the current PR, it seems to me that the approach basically computes a union of recirculated fields among all recirculated packets. After the pass, compiler loses the field list information of each individual recirculate operation. It makes compiling v1model on certain target significantly more difficult.
If we do decide to go with the annotation approach (if that makes bmv2 implementation easier), I would suggest we have more information in the @recirculate
annotation, for instance, @recirculate(1)
, @resubmit(1)
, @clone_i2e(1)
, @clone_e2e(1)
, where the number 1
can be used to differentiate the subset of fields used in different invocation of the primitives.
That said, if we have a way to make the original primitives work, that would be ideal.
Here is a presentation that describes the alternative possible implementations. |
@mbudiu-vmw In your recirculate.pdf slides, on slide 3, you mention that this code
is equivalent to this code:
I would agree with that, if the parameter of Would you agree that statement is not true in general if the parameter of I believe some people may be reading that slide, and think you are implying those two code snippets are equivalent regardless of the direction of the parameter of the extern function. |
Certainly, but in v1model recirculate did have an
|
Here are a P4_14 and some P4_16 example programs that do resubmit operations, with four different cases for the user-defined metadata that each of the four different resubmit operations perform. They are just toy programs, but the first P4_14 one and the fanciful, imagined, last P4_16 version should process packets identically, given a proper implementation of the imagined new language constructs used in that last version, which admittedly is most similar to how PSA handles it, with extra parameters to the ingress parser and ingress control. The P4_16 example could fairly easily be modified to use Mihai's idea of an extra extern to carry the preserved metadata, instead of additional parameters. That is a pretty minor variation. |
We can actually do this using annotations on the user metadata solely.
This is the only thing needed, plus a call to the resubmit method (or setting a resubmit field in the standard_metadata) with a corresponding integer argument, which should probably be restricted to be a compile-time constant. |
From this code we could generate entirely code equivalent with the fourth example. |
Here is an imagined P4_16 + v1model architecture program that uses the The only down side I can think of now for this approach are if you have annotations on fields in nested structs within the user-defined metadata, as shown in the example on this earlier comment: #1698 (comment) I personally do not think that is a deal-breaker, but wanted to point it out to others who may not have thought of it before. The other is that for recirculated packets and ingress-to-egress clones, this approach only works if the user-defined metadata is restricted to be the same type for ingress and egress parsers and controls. This is true for the v1model architecture, so no problem there. It is currently not the case for the PSA architecture, but it defines a completely different way to preserve user-defined metadata for such packets. If people like this annotation approach even better than the method used in PSA for preserving user-defined metadata, and wanted to change PSA so that it also uses this technique, we could consider adding that restriction to PSA. |
This is strictly for v1model. |
I would add that a resubmit annotation can have multiple arguments, if the respective field can participate in multiple "field lists". |
If there is no list the meaning could be that the field is always resubmitted... |
@jafingerhut : I have re-implemented this along the lines you have suggested recently.
With this change the tests using recirculate/clone/resubmit pass. |
@mbudiu-vmw Nice work! I noticed that some of the test P4_14 programs like copy_to_cpu.p4 and packet_redirect.p4 have standard_metadata in their field lists for fields to preserve with recirculated/resubmitted/cloned packets. If this approach goes forward, would attempting to compile such programs perhaps produce at least a warning, and perhaps an error, that they are not supported? In general, if any field was in such a field list that was not a user-defined metadata field? |
Also note that for digest calls, having a packet header field in them is a normal and expected thing, whereas they are not allowed in recirc/resub/clone field lists according to the P4_14 language spec. Example program issue1058.p4 creates a digest with a packet header field as one of the fields in its field lists, as well as all standard_metadata fields. I am not sure whether handling packet header fields presents any difficulties for this approach on digest function calls, or not. |
Also if this goes forward, we should review whether BMv2 simple_switch requires non-0 values for these field_list ids in the P4_16 source code, or whether it will cleanly handle 0 already. |
The field list id in the p4-16 source code has no relationship with the one generated in the JSON. |
This has been rebased and converted into #2902; this PR should be closed, but I will leave it for a while to preserve the discussion. |
This implements a proposal that performs recirculate by using tags on the user metadata fields.
If one calls
recirculate()
(with no arguments!), all fields that are tagged with@recirculate
are automatically recirculated at the end of the pipeline - similar with the P4-14 semantics.Limitations on the current implementation:
@recirculate
annotation on the corresponding metadata parameter when implementing the ingress controlThis is meant to address #1669.
This has been a long-standing bug which only surfaced recently. We may want to discuss this proposal in the language design working group, although it is really architecture-specific.