ONOS-7001 Support for direct counters
Currently Bmv2 returns UNKNOWN error when reading direct counters.
Change-Id: I834d7b5a8627181c6888500545e1bdbfe9af8dc1
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4Interpreter.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4Interpreter.java
index 6029590..da56bfc 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4Interpreter.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4Interpreter.java
@@ -43,6 +43,8 @@
import org.onosproject.net.pi.runtime.PiActionId;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.net.pi.runtime.PiActionParamId;
+import org.onosproject.net.pi.runtime.PiCounterId;
+import org.onosproject.net.pi.runtime.PiCounterType;
import org.onosproject.net.pi.runtime.PiHeaderFieldId;
import org.onosproject.net.pi.runtime.PiPacketMetadata;
import org.onosproject.net.pi.runtime.PiPacketMetadataId;
@@ -74,6 +76,7 @@
// e.g. in a dedicated onos/pipeconf directory, along with any related P4 source code.
public static final String TABLE0 = "table0";
+ public static final String TABLE0_COUNTER = "table0_counter";
public static final String SEND_TO_CPU = "send_to_cpu";
public static final String PORT = "port";
public static final String DROP = "_drop";
@@ -81,12 +84,17 @@
public static final String EGRESS_PORT = "egress_port";
public static final String INGRESS_PORT = "ingress_port";
+ private static final PiTableId TABLE0_ID = PiTableId.of(TABLE0);
+
protected static final PiHeaderFieldId ETH_DST_ID = PiHeaderFieldId.of("ethernet", "dstAddr");
protected static final PiHeaderFieldId ETH_SRC_ID = PiHeaderFieldId.of("ethernet", "srcAddr");
protected static final PiHeaderFieldId ETH_TYPE_ID = PiHeaderFieldId.of("ethernet", "etherType");
private static final ImmutableBiMap<Integer, PiTableId> TABLE_MAP = ImmutableBiMap.of(
- 0, PiTableId.of(TABLE0));
+ 0, TABLE0_ID);
+
+ private static final ImmutableBiMap<PiTableId, PiCounterId> TABLE_COUNTER_MAP = ImmutableBiMap.of(
+ TABLE0_ID, PiCounterId.of(TABLE0_COUNTER, PiCounterType.DIRECT));
private boolean targetAttributesInitialized = false;
@@ -189,6 +197,11 @@
}
@Override
+ public Optional<PiCounterId> mapTableCounter(PiTableId piTableId) {
+ return Optional.ofNullable(TABLE_COUNTER_MAP.get(piTableId));
+ }
+
+ @Override
public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
throws PiInterpreterException {
TrafficTreatment treatment = packet.treatment();
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4PortStatisticsDiscovery.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4PortStatisticsDiscovery.java
index 2946304..b127855 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4PortStatisticsDiscovery.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4PortStatisticsDiscovery.java
@@ -24,6 +24,7 @@
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiCounterCellId;
import org.onosproject.net.pi.runtime.PiCounterId;
+import org.onosproject.net.pi.runtime.PiIndirectCounterCellId;
import java.util.Collection;
import java.util.Collections;
@@ -32,6 +33,8 @@
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
+import static org.onosproject.net.pi.runtime.PiCounterType.INDIRECT;
+
/**
* Implementation of a PortStatisticsBehaviour that can be used for any P4 program based on default.p4 (i.e. those
* under onos/tools/test/p4src).
@@ -39,8 +42,8 @@
public class DefaultP4PortStatisticsDiscovery extends AbstractP4RuntimeHandlerBehaviour
implements PortStatisticsDiscovery {
- private static final PiCounterId INGRESS_COUNTER_ID = PiCounterId.of("ingress_port_counter");
- private static final PiCounterId EGRESS_COUNTER_ID = PiCounterId.of("egress_port_counter");
+ private static final PiCounterId INGRESS_COUNTER_ID = PiCounterId.of("ingress_port_counter", INDIRECT);
+ private static final PiCounterId EGRESS_COUNTER_ID = PiCounterId.of("egress_port_counter", INDIRECT);
@Override
public Collection<PortStatistics> discoverPortStatistics() {
@@ -54,14 +57,14 @@
deviceService.getPorts(deviceId)
.forEach(p -> portStatBuilders.put(p.number().toLong(),
DefaultPortStatistics.builder()
- .setPort((int) p.number().toLong())
+ .setPort(p.number())
.setDeviceId(deviceId)));
Set<PiCounterCellId> counterCellIds = Sets.newHashSet();
portStatBuilders.keySet().forEach(p -> {
// Counter cell/index = port number.
- counterCellIds.add(PiCounterCellId.of(INGRESS_COUNTER_ID, p));
- counterCellIds.add(PiCounterCellId.of(EGRESS_COUNTER_ID, p));
+ counterCellIds.add(PiIndirectCounterCellId.of(INGRESS_COUNTER_ID, p));
+ counterCellIds.add(PiIndirectCounterCellId.of(EGRESS_COUNTER_ID, p));
});
Collection<PiCounterCellData> counterEntryResponse;
@@ -73,20 +76,25 @@
return Collections.emptyList();
}
- counterEntryResponse.forEach(counterEntry -> {
- if (!portStatBuilders.containsKey(counterEntry.cellId().index())) {
- log.warn("Unrecognized counter index {}, skipping", counterEntry);
+ counterEntryResponse.forEach(counterData -> {
+ if (counterData.cellId().type() != INDIRECT) {
+ log.warn("Invalid counter data type {}, skipping", counterData.cellId().type());
return;
}
- DefaultPortStatistics.Builder statsBuilder = portStatBuilders.get(counterEntry.cellId().index());
- if (counterEntry.cellId().counterId().equals(INGRESS_COUNTER_ID)) {
- statsBuilder.setPacketsReceived(counterEntry.packets());
- statsBuilder.setBytesReceived(counterEntry.bytes());
- } else if (counterEntry.cellId().counterId().equals(EGRESS_COUNTER_ID)) {
- statsBuilder.setPacketsSent(counterEntry.packets());
- statsBuilder.setBytesSent(counterEntry.bytes());
+ PiIndirectCounterCellId indCellId = (PiIndirectCounterCellId) counterData.cellId();
+ if (!portStatBuilders.containsKey(indCellId.index())) {
+ log.warn("Unrecognized counter index {}, skipping", counterData);
+ return;
+ }
+ DefaultPortStatistics.Builder statsBuilder = portStatBuilders.get(indCellId.index());
+ if (counterData.cellId().counterId().equals(INGRESS_COUNTER_ID)) {
+ statsBuilder.setPacketsReceived(counterData.packets());
+ statsBuilder.setBytesReceived(counterData.bytes());
+ } else if (counterData.cellId().counterId().equals(EGRESS_COUNTER_ID)) {
+ statsBuilder.setPacketsSent(counterData.packets());
+ statsBuilder.setBytesSent(counterData.bytes());
} else {
- log.warn("Unrecognized counter ID {}, skipping", counterEntry);
+ log.warn("Unrecognized counter ID {}, skipping", counterData);
}
});
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
index ad9329a..50b808e 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import io.grpc.StatusRuntimeException;
import org.onosproject.net.flow.DefaultFlowEntry;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowRule;
@@ -26,6 +27,10 @@
import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.net.pi.model.PiPipelineModel;
import org.onosproject.net.pi.model.PiTableModel;
+import org.onosproject.net.pi.runtime.PiCounterCellData;
+import org.onosproject.net.pi.runtime.PiCounterCellId;
+import org.onosproject.net.pi.runtime.PiCounterId;
+import org.onosproject.net.pi.runtime.PiDirectCounterCellId;
import org.onosproject.net.pi.runtime.PiFlowRuleTranslationService;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.runtime.PiTableId;
@@ -36,6 +41,8 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.Lock;
@@ -72,6 +79,13 @@
// TODO: can remove this check as soon as the BMv2 bug when reading ECMP entries is fixed.
private boolean ignoreDeviceWhenGet = true;
+ /*
+ If true, we read all direct counters of a table with one request. Otherwise, send as many request as the number of
+ table entries.
+ */
+ // TODO: set to true as soon as the feature is implemented in P4Runtime.
+ private boolean readAllDirectCounters = false;
+
// Needed to synchronize operations over the same table entry.
private static final ConcurrentMap<P4RuntimeTableEntryReference, Lock> ENTRY_LOCKS = Maps.newConcurrentMap();
@@ -132,33 +146,70 @@
Collection<PiTableEntry> installedEntries;
try {
+ // TODO: optimize by dumping entries and counters in parallel, from ALL tables with the same request.
installedEntries = client.dumpTable(piTableId, pipeconf).get();
} catch (InterruptedException | ExecutionException e) {
- log.error("Exception while dumping table {} of {}", piTableId, deviceId, e);
+ if (!(e.getCause() instanceof StatusRuntimeException)) {
+ // gRPC errors are logged in the client.
+ log.error("Exception while dumping table {} of {}", piTableId, deviceId, e);
+ }
return Collections.emptyList();
}
+ Map<PiTableEntry, PiCounterCellData> counterCellMap;
+ try {
+ if (interpreter.mapTableCounter(piTableId).isPresent()) {
+ PiCounterId piCounterId = interpreter.mapTableCounter(piTableId).get();
+ Collection<PiCounterCellData> cellDatas;
+ if (readAllDirectCounters) {
+ cellDatas = client.readAllCounterCells(Collections.singleton(piCounterId), pipeconf).get();
+ } else {
+ Set<PiCounterCellId> cellIds = installedEntries.stream()
+ .map(entry -> PiDirectCounterCellId.of(piCounterId, entry))
+ .collect(Collectors.toSet());
+ cellDatas = client.readCounterCells(cellIds, pipeconf).get();
+ }
+ counterCellMap = cellDatas.stream()
+ .collect(Collectors.toMap(c -> ((PiDirectCounterCellId) c.cellId()).tableEntry(), c -> c));
+ } else {
+ counterCellMap = Collections.emptyMap();
+ }
+ installedEntries = client.dumpTable(piTableId, pipeconf).get();
+ } catch (InterruptedException | ExecutionException e) {
+ if (!(e.getCause() instanceof StatusRuntimeException)) {
+ // gRPC errors are logged in the client.
+ log.error("Exception while reading counters of table {} of {}", piTableId, deviceId, e);
+ }
+ counterCellMap = Collections.emptyMap();
+ }
+
for (PiTableEntry installedEntry : installedEntries) {
- P4RuntimeTableEntryReference entryRef = new P4RuntimeTableEntryReference(deviceId, piTableId,
+ P4RuntimeTableEntryReference entryRef = new P4RuntimeTableEntryReference(deviceId,
+ piTableId,
installedEntry.matchKey());
- P4RuntimeFlowRuleWrapper frWrapper = ENTRY_STORE.get(entryRef);
-
-
- if (frWrapper == null) {
+ if (!ENTRY_STORE.containsKey(entryRef)) {
// Inconsistent entry
inconsistentEntries.add(installedEntry);
continue; // next one.
}
- // TODO: implement table entry counter retrieval.
+ P4RuntimeFlowRuleWrapper frWrapper = ENTRY_STORE.get(entryRef);
+
long bytes = 0L;
long packets = 0L;
+ if (counterCellMap.containsKey(installedEntry)) {
+ PiCounterCellData counterCellData = counterCellMap.get(installedEntry);
+ bytes = counterCellData.bytes();
+ packets = counterCellData.packets();
+ }
- FlowEntry entry = new DefaultFlowEntry(frWrapper.rule(), ADDED, frWrapper.lifeInSeconds(),
- packets, bytes);
- resultBuilder.add(entry);
+ resultBuilder.add(new DefaultFlowEntry(frWrapper.rule(),
+ ADDED,
+ frWrapper.lifeInSeconds(),
+ packets,
+ bytes));
}
}