blob: 0bcfcafc39e92e7cf45bc13aa3460f0db8f1e171 [file] [log] [blame]
Frank Wangd7e3b4b2017-09-24 13:37:54 +09001/*
2 * Copyright 2017-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
Frank Wangd7e3b4b2017-09-24 13:37:54 +090019import com.google.common.cache.CacheBuilder;
20import com.google.common.cache.CacheLoader;
Carmelo Casconee5b28722018-06-22 17:28:28 +020021import com.google.common.cache.LoadingCache;
Wailok Shumf013a782021-07-26 16:51:01 +080022import com.google.common.collect.Sets;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090023import org.onosproject.drivers.p4runtime.mirror.P4RuntimeMeterMirror;
Wailok Shumf013a782021-07-26 16:51:01 +080024import org.onosproject.net.DeviceId;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090025import org.onosproject.net.meter.Band;
26import org.onosproject.net.meter.DefaultBand;
27import org.onosproject.net.meter.DefaultMeter;
Wailok Shumf013a782021-07-26 16:51:01 +080028import org.onosproject.net.meter.DefaultMeterFeatures;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090029import org.onosproject.net.meter.Meter;
Wailok Shumf013a782021-07-26 16:51:01 +080030import org.onosproject.net.meter.MeterFeatures;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090031import org.onosproject.net.meter.MeterOperation;
32import org.onosproject.net.meter.MeterProgrammable;
Wailok Shumf013a782021-07-26 16:51:01 +080033import org.onosproject.net.meter.MeterScope;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090034import org.onosproject.net.meter.MeterState;
35import org.onosproject.net.pi.model.PiMeterId;
36import org.onosproject.net.pi.model.PiMeterModel;
37import org.onosproject.net.pi.model.PiPipelineModel;
38import org.onosproject.net.pi.runtime.PiMeterCellConfig;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080039import org.onosproject.net.pi.runtime.PiMeterCellHandle;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090040import org.onosproject.net.pi.service.PiMeterTranslator;
41import org.onosproject.net.pi.service.PiTranslationException;
42
43import java.util.Collection;
44import java.util.Collections;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090045import java.util.HashSet;
Carmelo Casconee5b28722018-06-22 17:28:28 +020046import java.util.Set;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090047import java.util.concurrent.CompletableFuture;
Carmelo Casconee5b28722018-06-22 17:28:28 +020048import java.util.concurrent.TimeUnit;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090049import java.util.concurrent.locks.Lock;
50import java.util.concurrent.locks.ReentrantLock;
51import java.util.stream.Collectors;
52
Wailok Shumf013a782021-07-26 16:51:01 +080053import static com.google.common.base.Preconditions.checkNotNull;
54
Frank Wangd7e3b4b2017-09-24 13:37:54 +090055/**
56 * Implementation of MeterProgrammable behaviour for P4Runtime.
57 */
58public class P4RuntimeMeterProgrammable extends AbstractP4RuntimeHandlerBehaviour implements MeterProgrammable {
59
60 private static final int METER_LOCK_EXPIRE_TIME_IN_MIN = 10;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080061 private static final LoadingCache<PiMeterCellHandle, Lock>
Frank Wangd7e3b4b2017-09-24 13:37:54 +090062 ENTRY_LOCKS = CacheBuilder.newBuilder()
63 .expireAfterAccess(METER_LOCK_EXPIRE_TIME_IN_MIN, TimeUnit.MINUTES)
Carmelo Cascone4c289b72019-01-22 15:30:45 -080064 .build(new CacheLoader<PiMeterCellHandle, Lock>() {
Frank Wangd7e3b4b2017-09-24 13:37:54 +090065 @Override
Carmelo Cascone4c289b72019-01-22 15:30:45 -080066 public Lock load(PiMeterCellHandle handle) {
Frank Wangd7e3b4b2017-09-24 13:37:54 +090067 return new ReentrantLock();
68 }
69 });
70
71 private PiMeterTranslator translator;
72 private P4RuntimeMeterMirror meterMirror;
73 private PiPipelineModel pipelineModel;
74
75 @Override
Carmelo Casconec32976e2019-04-08 14:50:52 -070076 protected boolean setupBehaviour(String opName) {
77 if (!super.setupBehaviour(opName)) {
Frank Wangd7e3b4b2017-09-24 13:37:54 +090078 return false;
79 }
80
Yi Tsengd7716482018-10-31 15:34:30 -070081 translator = translationService.meterTranslator();
Frank Wangd7e3b4b2017-09-24 13:37:54 +090082 meterMirror = handler().get(P4RuntimeMeterMirror.class);
83 pipelineModel = pipeconf.pipelineModel();
84 return true;
85 }
86
87 @Override
88 public CompletableFuture<Boolean> performMeterOperation(MeterOperation meterOp) {
89
Carmelo Casconec32976e2019-04-08 14:50:52 -070090 if (!setupBehaviour("performMeterOperation()")) {
Kevin Chuangc267df22018-05-02 16:26:04 +080091 return CompletableFuture.completedFuture(false);
92 }
93
Frank Wangd7e3b4b2017-09-24 13:37:54 +090094 return CompletableFuture.completedFuture(processMeterOp(meterOp));
95 }
96
97 private boolean processMeterOp(MeterOperation meterOp) {
98
99 if (meterOp.type() != MeterOperation.Type.MODIFY) {
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800100 log.warn("P4Runtime meter operations must be MODIFY!");
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900101 return false;
102 }
103
104 PiMeterCellConfig piMeterCellConfig;
105 try {
106 piMeterCellConfig = translator.translate(meterOp.meter(), pipeconf);
107 } catch (PiTranslationException e) {
108 log.warn("Unable translate meter, aborting meter operation {}: {}", meterOp.type(), e.getMessage());
109 log.debug("exception", e);
110 return false;
111 }
112
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800113 final PiMeterCellHandle handle = PiMeterCellHandle.of(deviceId, piMeterCellConfig);
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900114 ENTRY_LOCKS.getUnchecked(handle).lock();
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700115 final boolean result = client.write(p4DeviceId, pipeconf)
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800116 .modify(piMeterCellConfig).submitSync().isSuccess();
Carmelo Casconee5b28722018-06-22 17:28:28 +0200117 if (result) {
118 meterMirror.put(handle, piMeterCellConfig);
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900119 }
Carmelo Casconee5b28722018-06-22 17:28:28 +0200120 ENTRY_LOCKS.getUnchecked(handle).unlock();
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900121
122 return result;
123 }
124
125 @Override
126 public CompletableFuture<Collection<Meter>> getMeters() {
127
Carmelo Casconec32976e2019-04-08 14:50:52 -0700128 if (!setupBehaviour("getMeters()")) {
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900129 return CompletableFuture.completedFuture(Collections.emptyList());
130 }
131
132 Collection<PiMeterCellConfig> piMeterCellConfigs;
133
134 Set<PiMeterId> meterIds = new HashSet<>();
135 for (PiMeterModel mode : pipelineModel.meters()) {
136 meterIds.add(mode.id());
137 }
138
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700139 piMeterCellConfigs = client.read(p4DeviceId, pipeconf)
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800140 .meterCells(meterIds).submitSync().all(PiMeterCellConfig.class);
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900141
142 Collection<Meter> meters = piMeterCellConfigs.stream()
143 .map(p -> {
144 DefaultMeter meter = (DefaultMeter) DefaultMeter.builder()
145 .withBands(p.meterBands().stream().map(b -> DefaultBand.builder()
146 .withRate(b.rate())
147 .burstSize(b.burst())
148 .ofType(Band.Type.NONE)
149 .build()).collect(Collectors.toList()))
150 .withCellId(p.cellId()).build();
151 meter.setState(MeterState.ADDED);
152 return meter;
153 })
154 .collect(Collectors.toList());
155
156 return CompletableFuture.completedFuture(meters);
157 }
Wailok Shumf013a782021-07-26 16:51:01 +0800158
159 @Override
160 public CompletableFuture<Collection<MeterFeatures>> getMeterFeatures() {
161
162 if (!setupBehaviour("getMeterFeatures()")) {
163 return CompletableFuture.completedFuture(Collections.emptyList());
164 }
165
166 Collection<MeterFeatures> meterFeatures = new HashSet<>();
167 pipeconf.pipelineModel().meters().forEach(
168 m -> meterFeatures.add(new P4RuntimeMeterFeaturesBuilder(m, deviceId).build()));
169
170 return CompletableFuture.completedFuture(meterFeatures);
171 }
172
173 /**
174 * P4 meter features builder.
175 */
176 public class P4RuntimeMeterFeaturesBuilder {
177 private final PiMeterModel piMeterModel;
178 private DeviceId deviceId;
179
180 private static final long PI_METER_START_INDEX = 0L;
181 private static final short PI_METER_MAX_BAND = 2;
182 private static final short PI_METER_MAX_COLOR = 3;
183
184 public P4RuntimeMeterFeaturesBuilder(PiMeterModel piMeterModel, DeviceId deviceId) {
185 this.piMeterModel = checkNotNull(piMeterModel);
186 this.deviceId = deviceId;
187 }
188
189 /**
190 * To build a MeterFeatures using the PiMeterModel object
191 * retrieved from pipeconf.
192 *
193 * @return the meter features object
194 */
195 public MeterFeatures build() {
196 /*
197 * We set the basic values before to extract the other information.
198 */
199 MeterFeatures.Builder builder = DefaultMeterFeatures.builder()
200 .forDevice(deviceId)
201 // The scope value will be PiMeterId
202 .withScope(MeterScope.of(piMeterModel.id().id()))
203 .withMaxBands(PI_METER_MAX_BAND)
204 .withMaxColors(PI_METER_MAX_COLOR)
205 .withStartIndex(PI_METER_START_INDEX)
206 .withEndIndex(piMeterModel.size() - 1);
207 /*
208 * Pi meter only support NONE type
209 */
210 Set<Band.Type> bands = Sets.newHashSet();
211 bands.add(Band.Type.NONE);
212 builder.withBandTypes(bands);
213 /*
214 * We extract the supported units;
215 */
216 Set<Meter.Unit> units = Sets.newHashSet();
217 if (piMeterModel.unit() == PiMeterModel.Unit.BYTES) {
218 units.add(Meter.Unit.KB_PER_SEC);
219 } else if (piMeterModel.unit() == PiMeterModel.Unit.PACKETS) {
220 units.add(Meter.Unit.PKTS_PER_SEC);
221 }
222 builder.withUnits(units);
223 /*
224 * Burst is supported ?
225 */
226 builder.hasBurst(true);
227 /*
228 * Stats are supported ?
229 */
230 builder.hasStats(false);
231
232 return builder.build();
233 }
234
235 /**
236 * To build an empty meter features.
237 * @param deviceId the device id
238 * @return the meter features
239 */
240 public MeterFeatures noMeterFeatures(DeviceId deviceId) {
241 return DefaultMeterFeatures.noMeterFeatures(deviceId);
242 }
243 }
Carmelo Casconee5b28722018-06-22 17:28:28 +0200244}