Get CPU port dynamically in fabric interpreter
Different HW platforms have different CPU ports, here we allow using
the same interpreter with multiple platform.
Change-Id: I0895d4d3e11560c75aca3fa908ca38759b597d67
diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipeconf.java b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipeconf.java
index 9b353c8..5fedaf7 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipeconf.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipeconf.java
@@ -100,6 +100,14 @@
/**
* Barefoot's Tofino context JSON.
*/
- TOFINO_CONTEXT_JSON
+ TOFINO_CONTEXT_JSON,
+
+ /**
+ * CPU port file in UTF 8 encoding.
+ */
+ // TODO: consider a better way to get the CPU port in the interpreter
+ // (see FabricInterpreter.java mapLogicalPortNumber). Perhaps using
+ // pipeconf annotations?
+ CPU_PORT_TXT
}
}
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java
index b3f996d..3115584 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java
@@ -36,12 +36,20 @@
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.pi.model.PiMatchFieldId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconfId;
import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.net.pi.model.PiTableId;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiControlMetadata;
import org.onosproject.net.pi.runtime.PiPacketOperation;
+import org.onosproject.net.pi.service.PiPipeconfService;
+import org.slf4j.Logger;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;
@@ -51,9 +59,12 @@
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.net.PortNumber.CONTROLLER;
import static org.onosproject.net.PortNumber.FLOOD;
import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
import static org.onosproject.net.pi.model.PiPacketOperationType.PACKET_OUT;
+import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.CPU_PORT_TXT;
+import static org.slf4j.LoggerFactory.getLogger;
/**
* Interpreter for fabric pipeline.
@@ -61,6 +72,8 @@
public class FabricInterpreter extends AbstractHandlerBehaviour
implements PiPipelineInterpreter {
+ private final Logger log = getLogger(getClass());
+
public static final int PORT_BITWIDTH = 9;
private static final ImmutableBiMap<Integer, PiTableId> TABLE_ID_MAP =
@@ -262,4 +275,55 @@
FabricConstants.INGRESS_PORT, deviceId, packetIn));
}
}
+
+ @Override
+ public Optional<Integer> mapLogicalPortNumber(PortNumber port) {
+ if (!port.equals(CONTROLLER)) {
+ return Optional.empty();
+ }
+ // This is probably brittle, but needed to dynamically get the CPU port
+ // for different platforms.
+ final DeviceId deviceId = data().deviceId();
+ final PiPipeconfService pipeconfService = handler().get(
+ PiPipeconfService.class);
+ final PiPipeconfId pipeconfId = pipeconfService
+ .ofDevice(deviceId).orElse(null);
+ if (pipeconfId == null ||
+ !pipeconfService.getPipeconf(pipeconfId).isPresent()) {
+ log.error("Unable to get pipeconf of {} - BUG?");
+ return Optional.empty();
+ }
+ final PiPipeconf pipeconf = pipeconfService.getPipeconf(pipeconfId).get();
+ if (!pipeconf.extension(CPU_PORT_TXT).isPresent()) {
+ log.error("Missing {} extension from pipeconf {}",
+ CPU_PORT_TXT, pipeconfId);
+ return Optional.empty();
+ }
+ return Optional.ofNullable(
+ readCpuPort(pipeconf.extension(CPU_PORT_TXT).get(),
+ pipeconfId));
+ }
+
+ private Integer readCpuPort(InputStream stream, PiPipeconfId pipeconfId) {
+ try {
+ final BufferedReader buff = new BufferedReader(
+ new InputStreamReader(stream));
+ final String str = buff.readLine();
+ buff.close();
+ if (str == null) {
+ log.error("Empty CPU port file for {}", pipeconfId);
+ return null;
+ }
+ try {
+ return Integer.parseInt(str);
+ } catch (NumberFormatException e) {
+ log.error("Invalid CPU port for {}: {}", pipeconfId, str);
+ return null;
+ }
+ } catch (IOException e) {
+ log.error("Unable to read CPU port file of {}: {}",
+ pipeconfId, e.getMessage());
+ return null;
+ }
+ }
}
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/PipeconfLoader.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/PipeconfLoader.java
index 3d04426..f808fc8 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/PipeconfLoader.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/PipeconfLoader.java
@@ -23,7 +23,6 @@
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.core.CoreService;
import org.onosproject.inbandtelemetry.api.IntProgrammable;
-import org.onosproject.net.PortNumber;
import org.onosproject.net.behaviour.Pipeliner;
import org.onosproject.net.device.PortStatisticsDiscovery;
import org.onosproject.net.pi.model.DefaultPiPipeconf;
@@ -44,7 +43,6 @@
import java.net.URL;
import java.util.Collection;
import java.util.Objects;
-import java.util.Optional;
import java.util.stream.Collectors;
import static java.lang.String.format;
@@ -79,13 +77,12 @@
private static final String DEFAULT_PLATFORM = "default";
private static final String BMV2_JSON = "bmv2.json";
private static final String P4INFO_TXT = "p4info.txt";
+ private static final String CPU_PORT_TXT = "cpu_port.txt";
private static final String TOFINO_BIN = "tofino.bin";
private static final String TOFINO_CTX_JSON = "context.json";
private static final String INT_PROFILE_SUFFIX = "-int";
private static final String FULL_PROFILE_SUFFIX = "-full";
- private static final int BMV2_CPU_PORT = 255;
-
private static final Collection<PiPipeconf> PIPECONFS = buildAllPipeconf();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -131,45 +128,56 @@
String profile = pieces[1];
String target = pieces[2];
String platform = pieces[3];
+ final DefaultPiPipeconf.Builder pipeconfBuilder;
try {
switch (target) {
case BMV2:
- return buildBmv2Pipeconf(profile, platform);
+ pipeconfBuilder = bmv2Pipeconf(profile, platform);
+ break;
case TOFINO:
- return buildTofinoPipeconf(profile, platform);
+ pipeconfBuilder = tofinoPipeconf(profile, platform);
+ break;
default:
log.warn("Unknown target '{}', skipping pipeconf build...",
target);
return null;
}
} catch (FileNotFoundException e) {
- log.warn("Unable to build pipeconf at {} because one or more p4c outputs are missing",
- path);
+ log.warn("Unable to build pipeconf at {} because file is missing: {}",
+ path, e.getMessage());
return null;
}
+ // Add IntProgrammable behaviour for INT-enabled profiles.
+ if (profile.endsWith(INT_PROFILE_SUFFIX) || profile.endsWith(FULL_PROFILE_SUFFIX)) {
+ pipeconfBuilder.addBehaviour(IntProgrammable.class, IntProgrammableImpl.class);
+ }
+ return pipeconfBuilder.build();
}
- private static PiPipeconf buildBmv2Pipeconf(String profile, String platform)
+ private static DefaultPiPipeconf.Builder bmv2Pipeconf(
+ String profile, String platform)
throws FileNotFoundException {
final URL bmv2JsonUrl = PipeconfLoader.class.getResource(format(
P4C_RES_BASE_PATH + BMV2_JSON, profile, BMV2, platform));
final URL p4InfoUrl = PipeconfLoader.class.getResource(format(
P4C_RES_BASE_PATH + P4INFO_TXT, profile, BMV2, platform));
- if (bmv2JsonUrl == null || p4InfoUrl == null) {
- throw new FileNotFoundException();
+ final URL cpuPortUrl = PipeconfLoader.class.getResource(format(
+ P4C_RES_BASE_PATH + CPU_PORT_TXT, profile, BMV2, platform));
+ if (bmv2JsonUrl == null) {
+ throw new FileNotFoundException(BMV2_JSON);
}
-
- DefaultPiPipeconf.Builder builder = basePipeconfBuilder(
- profile, platform, p4InfoUrl, Bmv2FabricInterpreter.class)
+ if (p4InfoUrl == null) {
+ throw new FileNotFoundException(P4INFO_TXT);
+ }
+ if (cpuPortUrl == null) {
+ throw new FileNotFoundException(CPU_PORT_TXT);
+ }
+ return basePipeconfBuilder(profile, platform, p4InfoUrl, cpuPortUrl)
.addExtension(ExtensionType.BMV2_JSON, bmv2JsonUrl);
- // Add IntProgrammable behaviour for INT-enabled profiles.
- if (profile.endsWith(INT_PROFILE_SUFFIX) || profile.endsWith(FULL_PROFILE_SUFFIX)) {
- builder.addBehaviour(IntProgrammable.class, IntProgrammableImpl.class);
- }
- return builder.build();
}
- private static PiPipeconf buildTofinoPipeconf(String profile, String platform)
+ private static DefaultPiPipeconf.Builder tofinoPipeconf(
+ String profile, String platform)
throws FileNotFoundException {
final URL tofinoBinUrl = PipeconfLoader.class.getResource(format(
P4C_RES_BASE_PATH + TOFINO_BIN, profile, TOFINO, platform));
@@ -177,19 +185,27 @@
P4C_RES_BASE_PATH + TOFINO_CTX_JSON, profile, TOFINO, platform));
final URL p4InfoUrl = PipeconfLoader.class.getResource(format(
P4C_RES_BASE_PATH + P4INFO_TXT, profile, TOFINO, platform));
- if (tofinoBinUrl == null || contextJsonUrl == null || p4InfoUrl == null) {
- throw new FileNotFoundException();
+ final URL cpuPortUrl = PipeconfLoader.class.getResource(format(
+ P4C_RES_BASE_PATH + CPU_PORT_TXT, profile, TOFINO, platform));
+ if (tofinoBinUrl == null) {
+ throw new FileNotFoundException(TOFINO_BIN);
}
- return basePipeconfBuilder(
- profile, platform, p4InfoUrl, FabricInterpreter.class)
+ if (contextJsonUrl == null) {
+ throw new FileNotFoundException(TOFINO_CTX_JSON);
+ }
+ if (p4InfoUrl == null) {
+ throw new FileNotFoundException(P4INFO_TXT);
+ }
+ if (cpuPortUrl == null) {
+ throw new FileNotFoundException(CPU_PORT_TXT);
+ }
+ return basePipeconfBuilder(profile, platform, p4InfoUrl, cpuPortUrl)
.addExtension(ExtensionType.TOFINO_BIN, tofinoBinUrl)
- .addExtension(ExtensionType.TOFINO_CONTEXT_JSON, contextJsonUrl)
- .build();
+ .addExtension(ExtensionType.TOFINO_CONTEXT_JSON, contextJsonUrl);
}
private static DefaultPiPipeconf.Builder basePipeconfBuilder(
- String profile, String platform, URL p4InfoUrl,
- Class<? extends FabricInterpreter> interpreterClass) {
+ String profile, String platform, URL p4InfoUrl, URL cpuPortUrl) {
final String pipeconfId = platform.equals(DEFAULT_PLATFORM)
// Omit platform if default, e.g. with BMv2 pipeconf
? format("%s.%s", BASE_PIPECONF_ID, profile)
@@ -199,12 +215,13 @@
.withId(new PiPipeconfId(pipeconfId))
.withPipelineModel(model)
.addBehaviour(PiPipelineInterpreter.class,
- interpreterClass)
+ FabricInterpreter.class)
.addBehaviour(Pipeliner.class,
FabricPipeliner.class)
.addBehaviour(PortStatisticsDiscovery.class,
FabricPortStatisticsDiscovery.class)
- .addExtension(ExtensionType.P4_INFO_TEXT, p4InfoUrl);
+ .addExtension(ExtensionType.P4_INFO_TEXT, p4InfoUrl)
+ .addExtension(ExtensionType.CPU_PORT_TXT, cpuPortUrl);
}
private static PiPipelineModel parseP4Info(URL p4InfoUrl) {
@@ -214,16 +231,4 @@
throw new IllegalStateException(e);
}
}
-
- // TODO: define interpreters with logical port mapping for Tofino platforms.
- public static class Bmv2FabricInterpreter extends FabricInterpreter {
- @Override
- public Optional<Integer> mapLogicalPortNumber(PortNumber port) {
- if (port.equals(PortNumber.CONTROLLER)) {
- return Optional.of(BMV2_CPU_PORT);
- } else {
- return Optional.empty();
- }
- }
- }
}
diff --git a/pipelines/fabric/src/main/resources/bmv2-compile.sh b/pipelines/fabric/src/main/resources/bmv2-compile.sh
index d92a5c4..b0ac43e 100755
--- a/pipelines/fabric/src/main/resources/bmv2-compile.sh
+++ b/pipelines/fabric/src/main/resources/bmv2-compile.sh
@@ -18,3 +18,5 @@
--p4runtime-file ${OUT_DIR}/p4info.txt \
--p4runtime-format text \
fabric.p4
+
+echo ${BMV2_CPU_PORT} > ${OUT_DIR}/cpu_port.txt
diff --git a/pipelines/fabric/src/main/resources/p4c-out/fabric-full/bmv2/default/cpu_port.txt b/pipelines/fabric/src/main/resources/p4c-out/fabric-full/bmv2/default/cpu_port.txt
new file mode 100644
index 0000000..ace9d03
--- /dev/null
+++ b/pipelines/fabric/src/main/resources/p4c-out/fabric-full/bmv2/default/cpu_port.txt
@@ -0,0 +1 @@
+255
diff --git a/pipelines/fabric/src/main/resources/p4c-out/fabric-int/bmv2/default/cpu_port.txt b/pipelines/fabric/src/main/resources/p4c-out/fabric-int/bmv2/default/cpu_port.txt
new file mode 100644
index 0000000..ace9d03
--- /dev/null
+++ b/pipelines/fabric/src/main/resources/p4c-out/fabric-int/bmv2/default/cpu_port.txt
@@ -0,0 +1 @@
+255
diff --git a/pipelines/fabric/src/main/resources/p4c-out/fabric-spgw/bmv2/default/cpu_port.txt b/pipelines/fabric/src/main/resources/p4c-out/fabric-spgw/bmv2/default/cpu_port.txt
new file mode 100644
index 0000000..ace9d03
--- /dev/null
+++ b/pipelines/fabric/src/main/resources/p4c-out/fabric-spgw/bmv2/default/cpu_port.txt
@@ -0,0 +1 @@
+255
diff --git a/pipelines/fabric/src/main/resources/p4c-out/fabric/bmv2/default/cpu_port.txt b/pipelines/fabric/src/main/resources/p4c-out/fabric/bmv2/default/cpu_port.txt
new file mode 100644
index 0000000..ace9d03
--- /dev/null
+++ b/pipelines/fabric/src/main/resources/p4c-out/fabric/bmv2/default/cpu_port.txt
@@ -0,0 +1 @@
+255