DSL Sketch #5
Replies: 4 comments 7 replies
-
Thanks for outlining this!! This is super helpful and makes things concrete to talk about. I want to propose a mid-level tweak here, which might help resolve the dropping/duplicating issue you highlight in Program 4. The idea is to further decouple the packet classification from the scheduling. Your proposal "bakes in" a specific notion of how to classify packets, based on source and destination address. My proposal is to make these classifications completely opaque. The result is a simpler language that is a little more annoying to use because you can't write In this model, every DSL program would start with a list of classes, something like this:
The idea is that the P4 program is required to attach a single integer variable with every packet, in this case ranging from 0 to 5. That number gets mapped to the 6 names declared at the top of the scheduler program. Part of the upside here is that we leave the classification entirely up to the P4 program, so it can classify packets based on anything a P4 program can do. So the associated P4 program could do P4y things to set the class: action set_schedule_class(bit<8> c) {
schedule_data.class = c;
}
table schedule_class {
key = { headers.source_addr; }
actions = { set_schedule_class; }
} I dunno, I'm really not a P4 programmer. But something like this. Now that we have opaque names for our classes, then back in our special new scheduling DSL, we amend the grammar to this:
So at the leaves, we write Here's where I was going with all this, however: we make it a correctness constraint that every class appears exactly once in the policy. (Classes are linear, if you like.) This sidesteps all possible complications w/r/t one class including another one and therefore conflicting—all classes are mutually exclusive, so you can never use one twice. And you are likewise not allowed to leave one off; if you want to drop packets, you should do that in your P4 program instead, I guess. Does this make sense? |
Beta Was this translation helpful? Give feedback.
-
I like it! I just wonder if we can bring back the "to drop a class of packets, just don't name it in the policy" thing. That is, we make it a correctness constraint that every class appears at most once in the policy. My angle here is that I don't want the P4 gadget to have the power to drop packets. I think a P4 gadget that just classifies packets would be more generic and more reusable. |
Beta Was this translation helpful? Give feedback.
-
Just writing out a quick idea that @sampsyo had during a meeting. The idea is for the DSL to be able to represent not only a source program (like a user might write) but also a program that has been compiled (in the Formal Abstractions sense) into a program that runs against a different topology but behaves identically. So for example, the user might write the policy
which is written against a ternary tree of height two. Then we might compile this policy so it runs against a binary tree of minimum height. The catch is that we still want to express that new policy in the DSL. That is, we want to be able to look at and study the compiled policy in a DSL before we compile it (in the Calyx sense) down to an accelerator. The compiled policy won't be as neat and tidy as our user-written one, but at least it'll be somewhat readable. We'll also be able to state and prove equivalence of two programs written in the same DSL. This is nicer than trying to directly prove that a program written in the DSL is equivalent to the program running on the accelerator. The challenge, of course, is that when we compile policies running on one topology to run on another, the new control program often needs to make scheduling decisions that "look through" intermediate nodes. The DSL as presented above is not immediately going to be able to do what this comment describes. We will need a careful tweak. |
Beta Was this translation helpful? Give feedback.
-
Just to keep this discussion current: we now have an AST and a parser; see the AST here. Reproducing it for easy reference: type clss = string
type var = string
type declare =
| DeclareClasses of clss list
type policy =
| Class of clss
| Fifo of policy list
| Fair of policy list
| Strict of policy list
| Var of var
type return =
| Return of policy
type assignment =
| Assn of var * policy
type program =
| Prog of declare * (assignment list) * return That is, you declare your classes once, make a series of assignments if you want, and then write a return statement with a policy as its payload. The idea is that the assignments will be for sub-policies, and that these will accrete into a policy that you will eventually return. I will note that If you actually want to combine two policies agnostically, i.e., you don't care about one crowding out another in the case of silences, you are welcome to write the policy |
Beta Was this translation helpful? Give feedback.
-
Background
As I mentioned towards the end of #3, we're eventually going to want a DSL that allows users to specify their policies without having to actually program (trees of) PIFOs. This DSL will expose exactly as much expressivity as we can support, after compilation, on the single PIFO tree that we will provision in hardware.
Here I have begun to sketch this DSL. Some of the policies it can express could already be supported on our simple existing hardware.
Syntax and Semantics
We assume for now that we only want to classify packets based on source and destination IP addresses. An address is constructed as:
Where
ip
is opaque and represented by capital letters, and the disjunction will become useful shortly.We have a packet classification pass that we are going to elide. The packet parser of a P4 program can do this easily enough with a match-action table. We denote the class of packets with source
addr_1
and destinationaddr_2
asaddr_1 -> addr_2
.For now we only support three policies.
That is:
fifo
: Take a given class of packets and just transmit it in first come, first served order.fair
: Alternate between items from each of the sub-policies in the list provided. This is work conserving.strict
: Strictly prefer packets from the first sub-policy in the list to those from the second, and so on.A program in our DSL will receive some number of classes of packets, each of the form
addr -> addr
. It must combine them into a single policy and return that policy. If classes of packets are never mentioned by the program, they will be dropped.Examples
Say our classifier has annotated the incoming packets in six possible ways:
Program 1: Forward packets having source
A
; drop the rest.When the disjunction covers all the possibilities, it may make sense to allow some syntactic sugar (e.g.,
A -> *
orA -> _
), but I'm just going to write it out in full for now.Program 2: Fairly split traffic by source.
Program 3: Fairly split traffic by source, and then prefer packets headed to
X
over those headed toY
.Program 3 is beginning to reveal an interesting problem. The policy we have written is well-formed, and is in fact a refinement of the policy we wrote in Program 2. However, this is because the sub-policies we wrote were carefully specifying details of larger "glob" policies. For example,
fifo (A -> X or Y)
in Program 2 is a glob, andleft
in Program 3 further refines it.Say a tired grad student wrote:
Program 4: Incorrect version of Program 3
Now I'm not sure what should happen to packets going from
A
toY
. I presume they get dropped. Should this result in a warning? Should we just catch it based on the fact thatfifo(B -> Y)
appears twice? Should we change the language to make such mistakes harder to make? Let's talk!Beta Was this translation helpful? Give feedback.
All reactions