forked from iovisor/bcc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request iovisor#293 from iovisor/bblanco_dev
added P4 deparser synthesis
- Loading branch information
Showing
6 changed files
with
504 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
# Copyright (c) Barefoot Networks, Inc. | ||
# Licensed under the Apache License, Version 2.0 (the "License") | ||
|
||
from collections import defaultdict, OrderedDict | ||
from p4_hlir.hlir import parse_call, p4_field, p4_parse_value_set, \ | ||
P4_DEFAULT, p4_parse_state, p4_table, \ | ||
p4_conditional_node, p4_parser_exception, \ | ||
p4_header_instance, P4_NEXT | ||
|
||
import ebpfProgram | ||
import ebpfInstance | ||
import ebpfType | ||
import ebpfStructType | ||
from topoSorting import Graph | ||
from programSerializer import ProgramSerializer | ||
|
||
def produce_parser_topo_sorting(hlir): | ||
# This function is copied from the P4 behavioral model implementation | ||
header_graph = Graph() | ||
|
||
def walk_rec(hlir, parse_state, prev_hdr_node, tag_stacks_index): | ||
assert(isinstance(parse_state, p4_parse_state)) | ||
for call in parse_state.call_sequence: | ||
call_type = call[0] | ||
if call_type == parse_call.extract: | ||
hdr = call[1] | ||
|
||
if hdr.virtual: | ||
base_name = hdr.base_name | ||
current_index = tag_stacks_index[base_name] | ||
if current_index > hdr.max_index: | ||
return | ||
tag_stacks_index[base_name] += 1 | ||
name = base_name + "[%d]" % current_index | ||
hdr = hlir.p4_header_instances[name] | ||
|
||
if hdr not in header_graph: | ||
header_graph.add_node(hdr) | ||
hdr_node = header_graph.get_node(hdr) | ||
|
||
if prev_hdr_node: | ||
prev_hdr_node.add_edge_to(hdr_node) | ||
else: | ||
header_graph.root = hdr | ||
prev_hdr_node = hdr_node | ||
|
||
for branch_case, next_state in parse_state.branch_to.items(): | ||
if not next_state: | ||
continue | ||
if not isinstance(next_state, p4_parse_state): | ||
continue | ||
walk_rec(hlir, next_state, prev_hdr_node, tag_stacks_index.copy()) | ||
|
||
start_state = hlir.p4_parse_states["start"] | ||
walk_rec(hlir, start_state, None, defaultdict(int)) | ||
|
||
header_topo_sorting = header_graph.produce_topo_sorting() | ||
|
||
return header_topo_sorting | ||
|
||
class EbpfDeparser(object): | ||
def __init__(self, hlir): | ||
header_topo_sorting = produce_parser_topo_sorting(hlir) | ||
self.headerOrder = [hdr.name for hdr in header_topo_sorting] | ||
|
||
def serialize(self, serializer, program): | ||
assert isinstance(serializer, ProgramSerializer) | ||
assert isinstance(program, ebpfProgram.EbpfProgram) | ||
|
||
serializer.emitIndent() | ||
serializer.blockStart() | ||
serializer.emitIndent() | ||
serializer.appendLine("/* Deparser */") | ||
serializer.emitIndent() | ||
serializer.appendFormat("{0} = 0;", program.offsetVariableName) | ||
serializer.newline() | ||
for h in self.headerOrder: | ||
header = program.getHeaderInstance(h) | ||
self.serializeHeaderEmit(header, serializer, program) | ||
serializer.blockEnd(True) | ||
|
||
def serializeHeaderEmit(self, header, serializer, program): | ||
assert isinstance(header, ebpfInstance.EbpfHeader) | ||
assert isinstance(serializer, ProgramSerializer) | ||
assert isinstance(program, ebpfProgram.EbpfProgram) | ||
p4header = header.hlirInstance | ||
assert isinstance(p4header, p4_header_instance) | ||
|
||
serializer.emitIndent() | ||
serializer.appendFormat("if ({0}.{1}.valid) ", | ||
program.headerStructName, header.name) | ||
serializer.blockStart() | ||
|
||
if ebpfProgram.EbpfProgram.isArrayElementInstance(p4header): | ||
ebpfStack = program.getStackInstance(p4header.base_name) | ||
assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack) | ||
|
||
if isinstance(p4header.index, int): | ||
index = "[" + str(headerInstance.index) + "]" | ||
elif p4header.index is P4_NEXT: | ||
index = "[" + ebpfStack.indexVar + "]" | ||
else: | ||
raise CompilationException( | ||
True, "Unexpected index for array {0}", | ||
p4header.index) | ||
basetype = ebpfStack.basetype | ||
else: | ||
ebpfHeader = program.getHeaderInstance(p4header.name) | ||
basetype = ebpfHeader.type | ||
index = "" | ||
|
||
alignment = 0 | ||
for field in basetype.fields: | ||
assert isinstance(field, ebpfStructType.EbpfField) | ||
|
||
self.serializeFieldEmit(serializer, p4header.base_name, | ||
index, field, alignment, program) | ||
alignment += field.widthInBits() | ||
alignment = alignment % 8 | ||
serializer.blockEnd(True) | ||
|
||
def serializeFieldEmit(self, serializer, name, index, | ||
field, alignment, program): | ||
assert isinstance(index, str) | ||
assert isinstance(name, str) | ||
assert isinstance(field, ebpfStructType.EbpfField) | ||
assert isinstance(serializer, ProgramSerializer) | ||
assert isinstance(alignment, int) | ||
assert isinstance(program, ebpfProgram.EbpfProgram) | ||
|
||
if field.name == "valid": | ||
return | ||
|
||
fieldToEmit = (program.headerStructName + "." + name + | ||
index + "." + field.name) | ||
width = field.widthInBits() | ||
if width <= 32: | ||
store = self.generatePacketStore(fieldToEmit, 0, alignment, | ||
width, program) | ||
serializer.emitIndent() | ||
serializer.appendLine(store) | ||
else: | ||
# Destination is bigger than 4 bytes and | ||
# represented as a byte array. | ||
b = (width + 7) / 8 | ||
for i in range(0, b): | ||
serializer.emitIndent() | ||
store = self.generatePacketStore(fieldToEmit + "["+str(i)+"]", | ||
i, | ||
alignment, | ||
8, program) | ||
serializer.appendLine(store) | ||
|
||
serializer.emitIndent() | ||
serializer.appendFormat("{0} += {1};", | ||
program.offsetVariableName, width) | ||
serializer.newline() | ||
|
||
def generatePacketStore(self, value, offset, alignment, width, program): | ||
assert width > 0 | ||
assert alignment < 8 | ||
assert isinstance(width, int) | ||
assert isinstance(alignment, int) | ||
|
||
return "bpf_dins_pkt({0}, {1} / 8 + {2}, {3}, {4}, {5});".format( | ||
program.packetName, | ||
program.offsetVariableName, | ||
offset, | ||
alignment, | ||
width, | ||
value | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# Copyright 2013-present Barefoot Networks, Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http:https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
# | ||
# Antonin Bas ([email protected]) | ||
# | ||
# | ||
|
||
# -*- coding: utf-8 -*- | ||
|
||
|
||
class Node(object): | ||
def __init__(self, n): | ||
self.n = n | ||
self.edges = set() | ||
|
||
def add_edge_to(self, other): | ||
assert(isinstance(other, Node)) | ||
self.edges.add(other) | ||
|
||
def __str__(self): | ||
return str(self.n) | ||
|
||
|
||
class Graph(object): | ||
def __init__(self): | ||
self.nodes = {} | ||
self.root = None | ||
|
||
def add_node(self, node): | ||
assert(node not in self.nodes) | ||
self.nodes[node] = Node(node) | ||
|
||
def __contains__(self, node): | ||
return node in self.nodes | ||
|
||
def get_node(self, node): | ||
return self.nodes[node] | ||
|
||
def produce_topo_sorting(self): | ||
def visit(node, topo_sorting, sequence=None): | ||
if sequence is not None: | ||
sequence += [str(node)] | ||
if node._behavioral_topo_sorting_mark == 1: | ||
if sequence is not None: | ||
print "cycle", sequence | ||
return False | ||
if node._behavioral_topo_sorting_mark != 2: | ||
node._behavioral_topo_sorting_mark = 1 | ||
for next_node in node.edges: | ||
res = visit(next_node, topo_sorting, sequence) | ||
if not res: | ||
return False | ||
node._behavioral_topo_sorting_mark = 2 | ||
topo_sorting.insert(0, node.n) | ||
return True | ||
|
||
has_cycle = False | ||
topo_sorting = [] | ||
|
||
for node in self.nodes.values(): | ||
# 0 is unmarked, 1 is temp, 2 is permanent | ||
node._behavioral_topo_sorting_mark = 0 | ||
for node in self.nodes.values(): | ||
if node._behavioral_topo_sorting_mark == 0: | ||
if not visit(node, topo_sorting, sequence=[]): | ||
has_cycle = True | ||
break | ||
# removing mark | ||
for node in self.nodes.values(): | ||
del node._behavioral_topo_sorting_mark | ||
|
||
if has_cycle: | ||
return None | ||
|
||
return topo_sorting | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../../../../bpf-docs/p4toEbpf-bcc.pdf |
Oops, something went wrong.