Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package org.onosproject.net.pi.impl; |
| 18 | |
Carmelo Cascone | a73f106 | 2018-07-16 22:49:46 +0200 | [diff] [blame] | 19 | import com.google.common.collect.Sets; |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 20 | import org.onosproject.net.Device; |
| 21 | import org.onosproject.net.group.Group; |
| 22 | import org.onosproject.net.group.GroupBucket; |
Carmelo Cascone | a73f106 | 2018-07-16 22:49:46 +0200 | [diff] [blame] | 23 | import org.onosproject.net.group.GroupDescription; |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 24 | import org.onosproject.net.pi.model.PiActionProfileId; |
| 25 | import org.onosproject.net.pi.model.PiActionProfileModel; |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 26 | import org.onosproject.net.pi.model.PiPipeconf; |
| 27 | import org.onosproject.net.pi.model.PiPipelineInterpreter; |
| 28 | import org.onosproject.net.pi.runtime.PiAction; |
Carmelo Cascone | cb4327a | 2018-09-11 15:17:23 -0700 | [diff] [blame] | 29 | import org.onosproject.net.pi.runtime.PiActionProfileGroup; |
| 30 | import org.onosproject.net.pi.runtime.PiActionProfileGroupId; |
| 31 | import org.onosproject.net.pi.runtime.PiActionProfileMember; |
| 32 | import org.onosproject.net.pi.runtime.PiActionProfileMemberId; |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 33 | import org.onosproject.net.pi.runtime.PiGroupKey; |
| 34 | import org.onosproject.net.pi.runtime.PiTableAction; |
Carmelo Cascone | 326ad2d | 2017-11-28 18:09:13 -0800 | [diff] [blame] | 35 | import org.onosproject.net.pi.service.PiTranslationException; |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 36 | |
Carmelo Cascone | a4a89fb | 2019-08-20 02:03:10 -0700 | [diff] [blame] | 37 | import java.util.Objects; |
Carmelo Cascone | a73f106 | 2018-07-16 22:49:46 +0200 | [diff] [blame] | 38 | import java.util.Set; |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 39 | |
| 40 | import static java.lang.String.format; |
Carmelo Cascone | 326ad2d | 2017-11-28 18:09:13 -0800 | [diff] [blame] | 41 | import static org.onosproject.net.pi.impl.PiFlowRuleTranslatorImpl.translateTreatment; |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 42 | import static org.onosproject.net.pi.impl.PiUtils.getInterpreterOrNull; |
| 43 | import static org.onosproject.net.pi.runtime.PiTableAction.Type.ACTION; |
| 44 | |
| 45 | /** |
| 46 | * Implementation of group translation logic. |
| 47 | */ |
Carmelo Cascone | 326ad2d | 2017-11-28 18:09:13 -0800 | [diff] [blame] | 48 | final class PiGroupTranslatorImpl { |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 49 | |
Carmelo Cascone | a73f106 | 2018-07-16 22:49:46 +0200 | [diff] [blame] | 50 | private static final Set<GroupDescription.Type> SUPPORTED_GROUP_TYPES = |
| 51 | Sets.immutableEnumSet( |
| 52 | GroupDescription.Type.SELECT, |
| 53 | GroupDescription.Type.INDIRECT); |
| 54 | |
Carmelo Cascone | 326ad2d | 2017-11-28 18:09:13 -0800 | [diff] [blame] | 55 | private PiGroupTranslatorImpl() { |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 56 | // Hides constructor. |
| 57 | } |
| 58 | |
| 59 | /** |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 60 | * Returns a PI action profile group equivalent to the given group, for the |
| 61 | * given pipeconf and device. |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 62 | * |
| 63 | * @param group group |
| 64 | * @param pipeconf pipeconf |
| 65 | * @param device device |
Carmelo Cascone | cb4327a | 2018-09-11 15:17:23 -0700 | [diff] [blame] | 66 | * @return PI action profile group |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 67 | * @throws PiTranslationException if the group cannot be translated |
| 68 | */ |
Carmelo Cascone | cb4327a | 2018-09-11 15:17:23 -0700 | [diff] [blame] | 69 | static PiActionProfileGroup translate(Group group, PiPipeconf pipeconf, Device device) |
| 70 | throws PiTranslationException { |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 71 | |
Carmelo Cascone | a73f106 | 2018-07-16 22:49:46 +0200 | [diff] [blame] | 72 | if (!SUPPORTED_GROUP_TYPES.contains(group.type())) { |
| 73 | throw new PiTranslationException(format( |
| 74 | "group type %s not supported", group.type())); |
| 75 | } |
| 76 | |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 77 | // Get action profile from group key. |
| 78 | // TODO: define proper field in group class. |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 79 | if (!(group.appCookie() instanceof PiGroupKey)) { |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 80 | throw new PiTranslationException( |
| 81 | "group app cookie is not PI (class should be PiGroupKey)"); |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 82 | } |
| 83 | final PiGroupKey groupKey = (PiGroupKey) group.appCookie(); |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 84 | final PiActionProfileId actionProfileId = groupKey.actionProfileId(); |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 85 | |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 86 | // Check validity of action profile against pipeconf. |
| 87 | final PiActionProfileModel actionProfileModel = pipeconf.pipelineModel() |
| 88 | .actionProfiles(actionProfileId) |
| 89 | .orElseThrow(() -> new PiTranslationException(format( |
| 90 | "no such action profile '%s'", actionProfileId))); |
| 91 | if (!actionProfileModel.hasSelector()) { |
| 92 | throw new PiTranslationException(format( |
| 93 | "action profile '%s' does not support dynamic selection", |
| 94 | actionProfileId)); |
| 95 | } |
| 96 | |
Daniele Moro | d900fe4 | 2021-02-11 16:12:57 +0100 | [diff] [blame] | 97 | // Check if the table associated with the action profile supports only |
| 98 | // one-shot action profile programming. |
| 99 | boolean isTableOneShot = actionProfileModel.tables().stream() |
| 100 | .map(tableId -> pipeconf.pipelineModel().table(tableId)) |
| 101 | .allMatch(piTableModel -> piTableModel.isPresent() && |
| 102 | piTableModel.get().oneShotOnly()); |
| 103 | if (isTableOneShot) { |
| 104 | throw new PiTranslationException(format( |
| 105 | "Table associated to action profile '%s' supports only one-shot action profile programming", |
| 106 | actionProfileId)); |
| 107 | } |
| 108 | |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 109 | // Check group validity. |
| 110 | if (actionProfileModel.maxGroupSize() > 0 |
| 111 | && group.buckets().buckets().size() > actionProfileModel.maxGroupSize()) { |
| 112 | throw new PiTranslationException(format( |
| 113 | "too many buckets, max group size for action profile '%s' is %d", |
| 114 | actionProfileId, actionProfileModel.maxGroupSize())); |
| 115 | } |
| 116 | |
Carmelo Cascone | 4f73fac | 2019-03-19 15:57:59 -0700 | [diff] [blame] | 117 | // If not INDIRECT, we set the maximum group size as specified in the |
| 118 | // model, however this might be highly inefficient for some HW targets |
| 119 | // which pre-allocate resources for the whole group. |
| 120 | final int maxGroupSize = group.type() == GroupDescription.Type.INDIRECT |
Carmelo Cascone | a7e60c6 | 2019-03-21 17:58:12 -0700 | [diff] [blame] | 121 | ? 1 : actionProfileModel.maxGroupSize(); |
Carmelo Cascone | 4f73fac | 2019-03-19 15:57:59 -0700 | [diff] [blame] | 122 | |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 123 | final PiActionProfileGroup.Builder piActionGroupBuilder = PiActionProfileGroup.builder() |
| 124 | .withId(PiActionProfileGroupId.of(group.id().id())) |
| 125 | .withActionProfileId(groupKey.actionProfileId()) |
Carmelo Cascone | 4f73fac | 2019-03-19 15:57:59 -0700 | [diff] [blame] | 126 | .withMaxSize(maxGroupSize); |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 127 | |
| 128 | // Translate group buckets to PI group members |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 129 | final PiPipelineInterpreter interpreter = getInterpreterOrNull(device, pipeconf); |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 130 | short bucketIdx = 0; |
| 131 | for (GroupBucket bucket : group.buckets().buckets()) { |
| 132 | /* |
| 133 | FIXME: the way member IDs are computed can cause collisions! |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 134 | Problem: In P4Runtime action profile members, i.e. action buckets, |
| 135 | are associated to a numeric ID chosen at member insertion time. This |
| 136 | ID must be unique for the whole action profile (i.e. the group table |
| 137 | in OpenFlow). In ONOS, GroupBucket doesn't specify any ID. |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 138 | |
| 139 | Solutions: |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 140 | - Change GroupBucket API to force application wanting to perform |
| 141 | group operations to specify a member id. |
| 142 | - Maintain state to dynamically allocate/deallocate member IDs, e.g. |
| 143 | in a dedicated service, or in a P4Runtime Group Provider. |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 144 | |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 145 | Hack: Statically derive member ID by combining groupId and position |
| 146 | of the bucket in the list. |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 147 | */ |
Carmelo Cascone | a4a89fb | 2019-08-20 02:03:10 -0700 | [diff] [blame] | 148 | final int memberId = Objects.hash(group.id(), bucketIdx); |
| 149 | if (memberId == 0) { |
| 150 | throw new PiTranslationException( |
| 151 | "GroupBucket produces PiActionProfileMember " + |
| 152 | "with invalid ID 0"); |
| 153 | } |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 154 | bucketIdx++; |
| 155 | |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 156 | final PiTableAction tableAction = translateTreatment( |
| 157 | bucket.treatment(), interpreter, |
| 158 | groupKey.tableId(), pipeconf.pipelineModel()); |
wu | 66460c9 | 2018-10-23 11:36:30 +0800 | [diff] [blame] | 159 | if (tableAction == null) { |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 160 | throw new PiTranslationException( |
| 161 | "bucket treatment translator returned null"); |
wu | 66460c9 | 2018-10-23 11:36:30 +0800 | [diff] [blame] | 162 | } |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 163 | |
| 164 | if (tableAction.type() != ACTION) { |
| 165 | throw new PiTranslationException(format( |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 166 | "action of type '%s' cannot be used in action profile members", |
| 167 | tableAction.type())); |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 168 | } |
| 169 | |
Carmelo Cascone | 99c59db | 2019-01-17 15:39:35 -0800 | [diff] [blame] | 170 | final PiActionProfileMember member = PiActionProfileMember.builder() |
| 171 | .forActionProfile(groupKey.actionProfileId()) |
| 172 | .withId(PiActionProfileMemberId.of(memberId)) |
| 173 | .withAction((PiAction) tableAction) |
| 174 | .build(); |
| 175 | |
pierventre | 121ea29 | 2021-08-03 18:05:42 +0200 | [diff] [blame] | 176 | // NOTE Indirect groups have weight set to -1 which is not supported |
| 177 | // by P4RT - setting to 1 to avoid problems with the p4rt server. |
| 178 | final int weight = group.type() == GroupDescription.Type.INDIRECT ? 1 : bucket.weight(); |
| 179 | piActionGroupBuilder.addMember(member, weight); |
Carmelo Cascone | 87b9b39 | 2017-10-02 18:33:20 +0200 | [diff] [blame] | 180 | } |
| 181 | |
| 182 | return piActionGroupBuilder.build(); |
| 183 | } |
| 184 | } |