blob: ecdda0825e4ffbb23737852208d85d316dd102eb [file] [log] [blame]
Carmelo Casconee44592f2018-09-12 02:24:47 -07001/*
2 * Copyright 2018-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
17package org.onosproject.drivers.p4runtime;
18
19import com.google.common.collect.ImmutableList;
20import com.google.common.util.concurrent.Striped;
21import org.onosproject.drivers.p4runtime.mirror.P4RuntimeMulticastGroupMirror;
22import org.onosproject.drivers.p4runtime.mirror.TimedEntry;
23import org.onosproject.net.DeviceId;
24import org.onosproject.net.group.DefaultGroup;
25import org.onosproject.net.group.Group;
26import org.onosproject.net.group.GroupDescription;
27import org.onosproject.net.group.GroupOperation;
28import org.onosproject.net.group.GroupOperations;
29import org.onosproject.net.group.GroupProgrammable;
30import org.onosproject.net.group.GroupStore;
31import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
32import org.onosproject.net.pi.runtime.PiMulticastGroupEntryHandle;
33import org.onosproject.net.pi.service.PiMulticastGroupTranslator;
34import org.onosproject.net.pi.service.PiTranslatedEntity;
35import org.onosproject.net.pi.service.PiTranslationException;
36import org.onosproject.p4runtime.api.P4RuntimeClient;
37
38import java.util.Collection;
39import java.util.Collections;
40import java.util.Objects;
41import java.util.Optional;
42import java.util.concurrent.locks.Lock;
43import java.util.stream.Collectors;
44
45import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.DELETE;
46import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.INSERT;
47import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.MODIFY;
48
49/**
50 * Implementation of GroupProgrammable to handle multicast groups in P4Runtime.
51 */
52public class P4RuntimeMulticastGroupProgrammable
53 extends AbstractP4RuntimeHandlerBehaviour implements GroupProgrammable {
54
55 // Needed to synchronize operations over the same group.
56 private static final Striped<Lock> STRIPED_LOCKS = Striped.lock(30);
57
58 private GroupStore groupStore;
59 private P4RuntimeMulticastGroupMirror mcGroupMirror;
60 private PiMulticastGroupTranslator mcGroupTranslator;
61
62 @Override
63 protected boolean setupBehaviour() {
64 if (!super.setupBehaviour()) {
65 return false;
66 }
67 mcGroupMirror = this.handler().get(P4RuntimeMulticastGroupMirror.class);
68 groupStore = handler().get(GroupStore.class);
Yi Tsengd7716482018-10-31 15:34:30 -070069 mcGroupTranslator = translationService.multicastGroupTranslator();
Carmelo Casconee44592f2018-09-12 02:24:47 -070070 return true;
71 }
72
73 @Override
74 public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
75 if (!setupBehaviour()) {
76 return;
77 }
78 groupOps.operations().stream()
79 .filter(op -> op.groupType().equals(GroupDescription.Type.ALL))
80 .forEach(op -> {
81 final Group group = groupStore.getGroup(deviceId, op.groupId());
82 processMcGroupOp(group, op.opType());
83 });
84 }
85
86 @Override
87 public Collection<Group> getGroups() {
88 if (!setupBehaviour()) {
89 return Collections.emptyList();
90 }
91 return ImmutableList.copyOf(getMcGroups());
92 }
93
94 private Collection<Group> getMcGroups() {
95 // TODO: missing support for reading multicast groups is ready in PI/Stratum.
96 return getMcGroupsFromMirror();
97 }
98
99 private Collection<Group> getMcGroupsFromMirror() {
100 return mcGroupMirror.getAll(deviceId).stream()
101 .map(TimedEntry::entry)
102 .map(this::forgeMcGroupEntry)
103 .filter(Objects::nonNull)
104 .collect(Collectors.toList());
105 }
106
107 private void processMcGroupOp(Group pdGroup, GroupOperation.Type opType) {
108 final PiMulticastGroupEntry mcGroup;
109 try {
110 mcGroup = mcGroupTranslator.translate(pdGroup, pipeconf);
111 } catch (PiTranslationException e) {
112 log.warn("Unable to translate multicast group, aborting {} operation: {} [{}]",
113 opType, e.getMessage(), pdGroup);
114 return;
115 }
116 final PiMulticastGroupEntryHandle handle = PiMulticastGroupEntryHandle.of(
117 deviceId, mcGroup);
118 final PiMulticastGroupEntry groupOnDevice = mcGroupMirror.get(handle) == null
119 ? null
120 : mcGroupMirror.get(handle).entry();
121 final Lock lock = STRIPED_LOCKS.get(handle);
122 lock.lock();
123 try {
124 processMcGroup(handle, mcGroup,
125 groupOnDevice, pdGroup, opType);
126 } finally {
127 lock.unlock();
128 }
129 }
130
131 private void processMcGroup(PiMulticastGroupEntryHandle handle,
132 PiMulticastGroupEntry groupToApply,
133 PiMulticastGroupEntry groupOnDevice,
134 Group pdGroup, GroupOperation.Type opType) {
135 switch (opType) {
136 case ADD:
137 robustMcGroupAdd(handle, groupToApply, pdGroup);
138 return;
139 case MODIFY:
140 // Since reading multicast groups is not supported yet on
141 // PI/Stratum, we cannot trust groupOnDevic) as we don't have a
142 // mechanism to enforce consistency of the mirror with the
143 // device state.
144 // if (driverBoolProperty(CHECK_MIRROR_BEFORE_UPDATE,
145 // DEFAULT_CHECK_MIRROR_BEFORE_UPDATE)
146 // && p4OpType == MODIFY
147 // && groupOnDevice != null
148 // && groupOnDevice.equals(groupToApply)) {
149 // // Ignore.
150 // return;
151 // }
152 robustMcGroupModify(handle, groupToApply, pdGroup);
153 return;
154 case DELETE:
155 mcGroupApply(handle, groupToApply, pdGroup, DELETE);
156 return;
157 default:
158 log.error("Unknown group operation type {}, " +
159 "cannot process multicast group", opType);
160 }
161 }
162
163 private boolean writeMcGroupOnDevice(PiMulticastGroupEntry group, P4RuntimeClient.WriteOperationType opType) {
164 return getFutureWithDeadline(
165 client.writePreMulticastGroupEntries(
166 Collections.singletonList(group), opType),
167 "performing multicast group " + opType, false);
168 }
169
170 private boolean mcGroupApply(PiMulticastGroupEntryHandle handle,
171 PiMulticastGroupEntry piGroup,
172 Group pdGroup,
173 P4RuntimeClient.WriteOperationType opType) {
174 switch (opType) {
175 case DELETE:
176 if (writeMcGroupOnDevice(piGroup, DELETE)) {
177 mcGroupMirror.remove(handle);
178 mcGroupTranslator.forget(handle);
179 return true;
180 } else {
181 return false;
182 }
183 case INSERT:
184 case MODIFY:
185 if (writeMcGroupOnDevice(piGroup, opType)) {
186 mcGroupMirror.put(handle, piGroup);
187 mcGroupTranslator.learn(handle, new PiTranslatedEntity<>(
188 pdGroup, piGroup, handle));
189 return true;
190 } else {
191 return false;
192 }
193 default:
194 log.warn("Unknown operation type {}, cannot apply group", opType);
195 return false;
196 }
197 }
198
199 private void robustMcGroupAdd(PiMulticastGroupEntryHandle handle,
200 PiMulticastGroupEntry piGroup,
201 Group pdGroup) {
202 if (mcGroupApply(handle, piGroup, pdGroup, INSERT)) {
203 return;
204 }
205 // Try to delete (perhaps it already exists) and re-add...
206 mcGroupApply(handle, piGroup, pdGroup, DELETE);
207 mcGroupApply(handle, piGroup, pdGroup, INSERT);
208 }
209
210 private void robustMcGroupModify(PiMulticastGroupEntryHandle handle,
211 PiMulticastGroupEntry piGroup,
212 Group pdGroup) {
213 if (mcGroupApply(handle, piGroup, pdGroup, MODIFY)) {
214 return;
215 }
216 // Not sure for which reason it cannot be modified, so try to delete and insert instead...
217 mcGroupApply(handle, piGroup, pdGroup, DELETE);
218 mcGroupApply(handle, piGroup, pdGroup, INSERT);
219 }
220
221 private Group forgeMcGroupEntry(PiMulticastGroupEntry mcGroup) {
222 final PiMulticastGroupEntryHandle handle = PiMulticastGroupEntryHandle.of(
223 deviceId, mcGroup);
224 final Optional<PiTranslatedEntity<Group, PiMulticastGroupEntry>>
225 translatedEntity = mcGroupTranslator.lookup(handle);
226 final TimedEntry<PiMulticastGroupEntry> timedEntry = mcGroupMirror.get(handle);
227 // Is entry consistent with our state?
228 if (!translatedEntity.isPresent()) {
229 log.warn("Multicast group handle not found in translation store: {}", handle);
230 return null;
231 }
232 if (timedEntry == null) {
233 log.warn("Multicast group handle not found in device mirror: {}", handle);
234 return null;
235 }
236 return addedGroup(translatedEntity.get().original(), timedEntry.lifeSec());
237 }
238
239 private Group addedGroup(Group original, long life) {
240 final DefaultGroup forgedGroup = new DefaultGroup(original.id(), original);
241 forgedGroup.setState(Group.GroupState.ADDED);
242 forgedGroup.setLife(life);
243 return forgedGroup;
244 }
245
246}