Skip to content

Commit e9ba39c

Browse files
axis0985ccascone
authored andcommitted
[ONOS-7808] Support P4Runtime default table entries
We achieve this by creating a special mirror to store the original default entries as specified in the P4 program. Applications can modify the default entry by inserting flow rules with empty selectors. When removing such flow rule, the default table entry is restored to the original one as stored in the mirror. Change-Id: Ib11a7172ab56be7cbbd23022e4b62ed6b70b6eca
1 parent 5a2de71 commit e9ba39c

File tree

20 files changed

+235
-89
lines changed

20 files changed

+235
-89
lines changed

drivers/bmv2/src/main/resources/bmv2-drivers.xml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
<behaviour api="org.onosproject.net.behaviour.PiPipelineProgrammable"
2020
impl="org.onosproject.drivers.bmv2.Bmv2PipelineProgrammable"/>
2121
<property name="tableDeleteBeforeUpdate">true</property>
22-
<property name="supportDefaultTableEntry">false</property>
2322
<!--
2423
<behaviour api="org.onosproject.net.group.GroupProgrammable"
2524
impl="org.onosproject.drivers.bmv2.Bmv2GroupProgrammable"/>
@@ -32,9 +31,6 @@
3231
hwVersion="BMv2 simple_switch" swVersion="Stratum" extends="stratum">
3332
<behaviour api="org.onosproject.net.behaviour.PiPipelineProgrammable"
3433
impl="org.onosproject.drivers.bmv2.Bmv2PipelineProgrammable"/>
35-
<!-- The current version of p4lang/PI used in Stratum does not
36-
support reading default table entries -->
37-
<property name="supportDefaultTableEntry">false</property>
3834
</driver>
3935
</drivers>
4036

drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/AbstractP4RuntimePipelineProgrammable.java

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,20 @@
1616

1717
package org.onosproject.drivers.p4runtime;
1818

19+
import org.onosproject.drivers.p4runtime.mirror.P4RuntimeDefaultEntryMirror;
1920
import org.onosproject.net.behaviour.PiPipelineProgrammable;
21+
import org.onosproject.net.pi.model.PiPipelineModel;
2022
import org.onosproject.net.pi.model.PiPipeconf;
23+
import org.onosproject.net.pi.runtime.PiTableEntry;
24+
import org.onosproject.p4runtime.api.P4RuntimeReadClient;
2125
import org.slf4j.Logger;
2226

2327
import java.nio.ByteBuffer;
2428
import java.util.Optional;
2529
import java.util.concurrent.CompletableFuture;
2630

31+
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverProperties.SUPPORT_DEFAULT_TABLE_ENTRY;
32+
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverProperties.DEFAULT_SUPPORT_DEFAULT_TABLE_ENTRY;
2733
import static java.util.concurrent.CompletableFuture.completedFuture;
2834
import static org.slf4j.LoggerFactory.getLogger;
2935

@@ -55,8 +61,9 @@ public CompletableFuture<Boolean> setPipeconf(PiPipeconf pipeconf) {
5561
// Hopefully the child class logged the problem.
5662
return completedFuture(false);
5763
}
58-
59-
return client.setPipelineConfig(p4DeviceId, pipeconf, deviceDataBuffer);
64+
CompletableFuture<Boolean> pipeconfSet = client.setPipelineConfig(
65+
p4DeviceId, pipeconf, deviceDataBuffer);
66+
return getDefaultEntries(pipeconfSet, pipeconf);
6067
}
6168

6269
@Override
@@ -70,4 +77,47 @@ public CompletableFuture<Boolean> isPipeconfSet(PiPipeconf pipeconf) {
7077

7178
@Override
7279
public abstract Optional<PiPipeconf> getDefaultPipeconf();
80+
81+
/**
82+
* Once the pipeconf is set successfully, we should store all the default entries
83+
* before notify other service to prevent overwriting the default entries.
84+
* Default entries may be used in P4RuntimeFlowRuleProgrammable.
85+
* <p>
86+
* This method returns a completable future with the result of the pipeconf set
87+
* operation (which might not be true).
88+
*
89+
* @param pipeconfSet completable future for setting pipeconf
90+
* @param pipeconf pipeconf
91+
* @return completable future eventually true if the pipeconf set successfully
92+
*/
93+
private CompletableFuture<Boolean> getDefaultEntries(CompletableFuture<Boolean> pipeconfSet, PiPipeconf pipeconf) {
94+
if (!driverBoolProperty(
95+
SUPPORT_DEFAULT_TABLE_ENTRY,
96+
DEFAULT_SUPPORT_DEFAULT_TABLE_ENTRY)) {
97+
return pipeconfSet;
98+
}
99+
return pipeconfSet.thenApply(setSuccess -> {
100+
if (!setSuccess) {
101+
return setSuccess;
102+
}
103+
final P4RuntimeDefaultEntryMirror mirror = handler()
104+
.get(P4RuntimeDefaultEntryMirror.class);
105+
106+
final PiPipelineModel pipelineModel = pipeconf.pipelineModel();
107+
final P4RuntimeReadClient.ReadRequest request = client.read(
108+
p4DeviceId, pipeconf);
109+
// Read default entries from all non-constant tables.
110+
// Ignore constant default entries.
111+
pipelineModel.tables().stream()
112+
.filter(t -> !t.isConstantTable())
113+
.forEach(t -> {
114+
if (!t.constDefaultAction().isPresent()) {
115+
request.defaultTableEntry(t.id());
116+
}
117+
});
118+
final P4RuntimeReadClient.ReadResponse response = request.submitSync();
119+
mirror.sync(deviceId, response.all(PiTableEntry.class));
120+
return true;
121+
});
122+
}
73123
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2019-present Open Networking Foundation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.onosproject.drivers.p4runtime;
18+
19+
/**
20+
* Driver properties for P4Runtime.
21+
*/
22+
public final class P4RuntimeDriverProperties {
23+
24+
// hide default constructor
25+
private P4RuntimeDriverProperties() {
26+
}
27+
28+
// When updating an existing rule, if true, we issue a DELETE operation
29+
// before inserting the new one, otherwise we issue a MODIFY operation. This
30+
// is useful fore devices that do not support MODIFY operations for table
31+
// entries.
32+
public static final String DELETE_BEFORE_UPDATE = "tableDeleteBeforeUpdate";
33+
public static final boolean DEFAULT_DELETE_BEFORE_UPDATE = false;
34+
35+
// If true, we avoid querying the device and return what's already known by
36+
// the ONOS store.
37+
public static final String READ_FROM_MIRROR = "tableReadFromMirror";
38+
public static final boolean DEFAULT_READ_FROM_MIRROR = false;
39+
40+
// If true, we read counters when reading table entries (if table has
41+
// counters). Otherwise, we don't.
42+
public static final String SUPPORT_TABLE_COUNTERS = "supportTableCounters";
43+
public static final boolean DEFAULT_SUPPORT_TABLE_COUNTERS = true;
44+
45+
// If true, assumes that the device returns table entry message populated
46+
// with direct counter values. If false, we issue a second P4Runtime request
47+
// to read the direct counter values.
48+
public static final String READ_COUNTERS_WITH_TABLE_ENTRIES = "tableReadCountersWithTableEntries";
49+
public static final boolean DEFAULT_READ_COUNTERS_WITH_TABLE_ENTRIES = true;
50+
51+
// True if target supports reading and writing table entries.
52+
public static final String SUPPORT_DEFAULT_TABLE_ENTRY = "supportDefaultTableEntry";
53+
public static final boolean DEFAULT_SUPPORT_DEFAULT_TABLE_ENTRY = true;
54+
}

drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java

Lines changed: 45 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@
2121
import com.google.common.collect.Maps;
2222
import com.google.common.util.concurrent.Futures;
2323
import com.google.common.util.concurrent.Striped;
24+
import org.onosproject.drivers.p4runtime.mirror.P4RuntimeDefaultEntryMirror;
2425
import org.onosproject.drivers.p4runtime.mirror.P4RuntimeTableMirror;
2526
import org.onosproject.drivers.p4runtime.mirror.TimedEntry;
2627
import org.onosproject.net.flow.DefaultFlowEntry;
2728
import org.onosproject.net.flow.FlowEntry;
2829
import org.onosproject.net.flow.FlowRule;
2930
import org.onosproject.net.flow.FlowRuleProgrammable;
3031
import org.onosproject.net.pi.model.PiCounterType;
31-
import org.onosproject.net.pi.model.PiPipelineInterpreter;
3232
import org.onosproject.net.pi.model.PiPipelineModel;
3333
import org.onosproject.net.pi.model.PiTableId;
3434
import org.onosproject.net.pi.runtime.PiCounterCell;
@@ -37,6 +37,7 @@
3737
import org.onosproject.net.pi.runtime.PiCounterCellId;
3838
import org.onosproject.net.pi.runtime.PiEntityType;
3939
import org.onosproject.net.pi.runtime.PiHandle;
40+
import org.onosproject.net.pi.runtime.PiMatchKey;
4041
import org.onosproject.net.pi.runtime.PiTableEntry;
4142
import org.onosproject.net.pi.runtime.PiTableEntryHandle;
4243
import org.onosproject.net.pi.service.PiFlowRuleTranslator;
@@ -58,7 +59,16 @@
5859
import java.util.concurrent.locks.Lock;
5960
import java.util.stream.Collectors;
6061

61-
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverUtils.getInterpreter;
62+
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverProperties.DEFAULT_DELETE_BEFORE_UPDATE;
63+
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverProperties.DEFAULT_READ_COUNTERS_WITH_TABLE_ENTRIES;
64+
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverProperties.DEFAULT_READ_FROM_MIRROR;
65+
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverProperties.DEFAULT_SUPPORT_DEFAULT_TABLE_ENTRY;
66+
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverProperties.DEFAULT_SUPPORT_TABLE_COUNTERS;
67+
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverProperties.DELETE_BEFORE_UPDATE;
68+
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverProperties.READ_COUNTERS_WITH_TABLE_ENTRIES;
69+
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverProperties.READ_FROM_MIRROR;
70+
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverProperties.SUPPORT_DEFAULT_TABLE_ENTRY;
71+
import static org.onosproject.drivers.p4runtime.P4RuntimeDriverProperties.SUPPORT_TABLE_COUNTERS;
6272
import static org.onosproject.drivers.p4runtime.P4RuntimeFlowRuleProgrammable.Operation.APPLY;
6373
import static org.onosproject.drivers.p4runtime.P4RuntimeFlowRuleProgrammable.Operation.REMOVE;
6474
import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
@@ -73,40 +83,14 @@ public class P4RuntimeFlowRuleProgrammable
7383
extends AbstractP4RuntimeHandlerBehaviour
7484
implements FlowRuleProgrammable {
7585

76-
// When updating an existing rule, if true, we issue a DELETE operation
77-
// before inserting the new one, otherwise we issue a MODIFY operation. This
78-
// is useful fore devices that do not support MODIFY operations for table
79-
// entries.
80-
private static final String DELETE_BEFORE_UPDATE = "tableDeleteBeforeUpdate";
81-
private static final boolean DEFAULT_DELETE_BEFORE_UPDATE = false;
82-
83-
// If true, we avoid querying the device and return what's already known by
84-
// the ONOS store.
85-
private static final String READ_FROM_MIRROR = "tableReadFromMirror";
86-
private static final boolean DEFAULT_READ_FROM_MIRROR = false;
87-
88-
// If true, we read counters when reading table entries (if table has
89-
// counters). Otherwise, we don't.
90-
private static final String SUPPORT_TABLE_COUNTERS = "supportTableCounters";
91-
private static final boolean DEFAULT_SUPPORT_TABLE_COUNTERS = true;
92-
93-
// If true, assumes that the device returns table entry message populated
94-
// with direct counter values. If false, we issue a second P4Runtime request
95-
// to read the direct counter values.
96-
private static final String READ_COUNTERS_WITH_TABLE_ENTRIES = "tableReadCountersWithTableEntries";
97-
private static final boolean DEFAULT_READ_COUNTERS_WITH_TABLE_ENTRIES = true;
98-
99-
// True if target supports reading and writing table entries.
100-
private static final String SUPPORT_DEFAULT_TABLE_ENTRY = "supportDefaultTableEntry";
101-
private static final boolean DEFAULT_SUPPORT_DEFAULT_TABLE_ENTRY = true;
102-
10386
// Used to make sure concurrent calls to write flow rules are serialized so
10487
// that each request gets consistent access to mirror state.
10588
private static final Striped<Lock> WRITE_LOCKS = Striped.lock(30);
10689

10790
private PiPipelineModel pipelineModel;
10891
private P4RuntimeTableMirror tableMirror;
10992
private PiFlowRuleTranslator translator;
93+
private P4RuntimeDefaultEntryMirror defaultEntryMirror;
11094

11195
@Override
11296
protected boolean setupBehaviour(String opName) {
@@ -118,6 +102,7 @@ protected boolean setupBehaviour(String opName) {
118102
pipelineModel = pipeconf.pipelineModel();
119103
tableMirror = handler().get(P4RuntimeTableMirror.class);
120104
translator = translationService.flowRuleTranslator();
105+
defaultEntryMirror = handler().get(P4RuntimeDefaultEntryMirror.class);
121106
return true;
122107
}
123108

@@ -128,7 +113,8 @@ public Collection<FlowEntry> getFlowEntries() {
128113
return Collections.emptyList();
129114
}
130115

131-
if (driverBoolProperty(READ_FROM_MIRROR, DEFAULT_READ_FROM_MIRROR)) {
116+
if (driverBoolProperty(READ_FROM_MIRROR,
117+
DEFAULT_READ_FROM_MIRROR)) {
132118
return getFlowEntriesFromMirror();
133119
}
134120

@@ -237,8 +223,12 @@ private FlowEntry forgeFlowEntry(PiTableEntry entry,
237223
translatedEntity = translator.lookup(handle);
238224
final TimedEntry<PiTableEntry> timedEntry = tableMirror.get(handle);
239225

226+
// A default entry might not be present in the translation store if it
227+
// was not inserted by an app. No need to log.
240228
if (!translatedEntity.isPresent()) {
241-
log.warn("Table entry handle not found in translation store: {}", handle);
229+
if (!isOriginalDefaultEntry(entry)) {
230+
log.warn("Table entry handle not found in translation store: {}", handle);
231+
}
242232
return null;
243233
}
244234
if (!translatedEntity.get().translated().equals(entry)) {
@@ -390,9 +380,11 @@ private boolean appendEntryToWriteRequestOrSkip(
390380
final UpdateType updateType;
391381

392382
final boolean supportDefaultEntry = driverBoolProperty(
393-
SUPPORT_DEFAULT_TABLE_ENTRY, DEFAULT_SUPPORT_DEFAULT_TABLE_ENTRY);
383+
SUPPORT_DEFAULT_TABLE_ENTRY,
384+
DEFAULT_SUPPORT_DEFAULT_TABLE_ENTRY);
394385
final boolean deleteBeforeUpdate = driverBoolProperty(
395-
DELETE_BEFORE_UPDATE, DEFAULT_DELETE_BEFORE_UPDATE);
386+
DELETE_BEFORE_UPDATE,
387+
DEFAULT_DELETE_BEFORE_UPDATE);
396388

397389
if (driverOperation == APPLY) {
398390
if (piEntryOnDevice == null) {
@@ -419,8 +411,8 @@ private boolean appendEntryToWriteRequestOrSkip(
419411
} else {
420412
// REMOVE.
421413
if (piEntryToApply.isDefaultAction()) {
422-
// Cannot remove default action. Instead we should use the
423-
// original defined by the interpreter (if any).
414+
// Cannot remove default action. Instead we should modify it to
415+
// use the original one as specified in the P4 program.
424416
final PiTableEntry originalDefaultEntry = getOriginalDefaultEntry(
425417
piEntryToApply.table());
426418
if (originalDefaultEntry == null) {
@@ -443,31 +435,32 @@ private boolean appendEntryToWriteRequestOrSkip(
443435
}
444436

445437
private PiTableEntry getOriginalDefaultEntry(PiTableId tableId) {
446-
final PiPipelineInterpreter interpreter = getInterpreter(handler());
447-
if (interpreter == null) {
448-
log.warn("Missing interpreter for {}, cannot get default action",
449-
deviceId);
450-
return null;
451-
}
452-
if (!interpreter.getOriginalDefaultAction(tableId).isPresent()) {
453-
log.warn("Interpreter of {} doesn't define a default action for " +
454-
"table {}, cannot produce default action entry",
455-
deviceId, tableId);
456-
return null;
457-
}
458-
return PiTableEntry.builder()
438+
final PiTableEntryHandle handle = PiTableEntry.builder()
459439
.forTable(tableId)
460-
.withAction(interpreter.getOriginalDefaultAction(tableId).get())
461-
.build();
440+
.withMatchKey(PiMatchKey.EMPTY)
441+
.build()
442+
.handle(deviceId);
443+
final TimedEntry<PiTableEntry> originalDefaultEntry = defaultEntryMirror.get(handle);
444+
if (originalDefaultEntry != null) {
445+
return originalDefaultEntry.entry();
446+
}
447+
return null;
462448
}
463449

464450
private boolean isOriginalDefaultEntry(PiTableEntry entry) {
465451
if (!entry.isDefaultAction()) {
466452
return false;
467453
}
468454
final PiTableEntry originalDefaultEntry = getOriginalDefaultEntry(entry.table());
469-
return originalDefaultEntry != null &&
470-
originalDefaultEntry.action().equals(entry.action());
455+
if (originalDefaultEntry == null) {
456+
return false;
457+
}
458+
// Sometimes the default action may be null
459+
// e.g. In basic pipeline, the default action in wcmp_table is null
460+
if (originalDefaultEntry.action() == null) {
461+
return entry.action() == null;
462+
}
463+
return originalDefaultEntry.action().equals(entry.action());
471464
}
472465

473466
private Map<PiTableEntryHandle, PiCounterCellData> readEntryCounters(
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2019-present Open Networking Foundation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.onosproject.drivers.p4runtime.mirror;
18+
19+
import org.onosproject.net.pi.runtime.PiEntityType;
20+
import org.onosproject.net.pi.runtime.PiTableEntry;
21+
import org.onosproject.net.pi.runtime.PiTableEntryHandle;
22+
import org.osgi.service.component.annotations.Component;
23+
24+
/**
25+
* Distributed implementation of a P4Runtime default entry mirror.
26+
*/
27+
@Component(immediate = true, service = P4RuntimeDefaultEntryMirror.class)
28+
public final class DistributedP4RuntimeDefaultEntryMirror
29+
extends AbstractDistributedP4RuntimeMirror
30+
<PiTableEntryHandle, PiTableEntry>
31+
implements P4RuntimeDefaultEntryMirror {
32+
33+
public DistributedP4RuntimeDefaultEntryMirror() {
34+
super(PiEntityType.TABLE_ENTRY);
35+
}
36+
}

0 commit comments

Comments
 (0)