go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/ifplugin/descriptor/rx_mode.go (about) 1 // Copyright (c) 2018 Cisco and/or its affiliates. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at: 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package descriptor 16 17 import ( 18 "fmt" 19 20 "github.com/pkg/errors" 21 "go.ligato.io/cn-infra/v2/logging" 22 "google.golang.org/protobuf/proto" 23 24 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 25 "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/descriptor/adapter" 26 "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx" 27 "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/vppcalls" 28 "go.ligato.io/vpp-agent/v3/proto/ligato/kvscheduler" 29 interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces" 30 ) 31 32 const ( 33 // RxModeDescriptorName is the name of the descriptor for the unnumbered 34 // config-subsection of VPP interfaces. 35 RxModeDescriptorName = "vpp-interface-rx-mode" 36 37 // dependency labels 38 linkIsUpDep = "interface-link-is-UP" 39 ) 40 41 // A list of non-retriable errors: 42 var ( 43 // ErrUnsupportedRxMode is returned when the given interface type does not support the chosen 44 // RX mode. 45 ErrUnsupportedRxMode = errors.New("unsupported RX Mode") 46 47 // ErrUndefinedRxMode is returned when the Rx mode is not defined. 48 ErrUndefinedRxMode = errors.New("undefined RX Mode") 49 50 // ErrUnsupportedRxMode is returned when Rx mode has multiple definitions for the same queue. 51 ErrRedefinedRxMode = errors.New("redefined RX Mode") 52 ) 53 54 // RxModeDescriptor configures Rx mode for VPP interface queues. 55 type RxModeDescriptor struct { 56 log logging.Logger 57 ifHandler vppcalls.InterfaceVppAPI 58 ifIndex ifaceidx.IfaceMetadataIndex 59 } 60 61 // NewRxModeDescriptor creates a new instance of RxModeDescriptor. 62 func NewRxModeDescriptor(ifHandler vppcalls.InterfaceVppAPI, ifIndex ifaceidx.IfaceMetadataIndex, 63 log logging.PluginLogger) *kvs.KVDescriptor { 64 65 ctx := &RxModeDescriptor{ 66 ifHandler: ifHandler, 67 ifIndex: ifIndex, 68 log: log.NewLogger("rx-mode-descriptor"), 69 } 70 71 typedDescr := &adapter.RxModeDescriptor{ 72 Name: RxModeDescriptorName, 73 KeySelector: ctx.IsInterfaceRxModeKey, 74 // proto message Interface is only used as container for RxMode 75 ValueTypeName: string(proto.MessageName(&interfaces.Interface{})), 76 ValueComparator: ctx.EquivalentRxMode, 77 Validate: ctx.Validate, 78 Create: ctx.Create, 79 Update: ctx.Update, 80 Delete: ctx.Delete, 81 Dependencies: ctx.Dependencies, 82 } 83 84 return adapter.NewRxModeDescriptor(typedDescr) 85 } 86 87 // IsInterfaceRxModeKey returns true if the key is identifying RxMode configuration. 88 func (d *RxModeDescriptor) IsInterfaceRxModeKey(key string) bool { 89 _, isValid := interfaces.ParseRxModesKey(key) 90 return isValid 91 } 92 93 // EquivalentRxMode compares Rx modes for equivalency. 94 func (d *RxModeDescriptor) EquivalentRxMode(key string, oldIntf, newIntf *interfaces.Interface) bool { 95 /* Note: default Rx mode cannot be dumped - compare only if these are two NB 96 configurations (not a refreshed, i.e. dumped, value). 97 */ 98 oldDefMode := getDefaultRxMode(oldIntf) 99 newDefMode := getDefaultRxMode(newIntf) 100 if oldDefMode != interfaces.Interface_RxMode_UNKNOWN && 101 newDefMode != interfaces.Interface_RxMode_UNKNOWN { 102 if oldDefMode != newDefMode { 103 return false 104 } 105 } 106 // compare queue-specific RX modes 107 for _, rxMode := range oldIntf.GetRxModes() { 108 if rxMode.DefaultMode { 109 continue 110 } 111 oldMode := normalizeRxMode(rxMode.Mode, oldIntf) 112 newMode := getQueueRxMode(rxMode.Queue, newIntf) 113 if oldMode != newMode { 114 return false 115 } 116 } 117 for _, rxMode := range newIntf.GetRxModes() { 118 if rxMode.DefaultMode { 119 continue 120 } 121 newMode := normalizeRxMode(rxMode.Mode, newIntf) 122 oldMode := getQueueRxMode(rxMode.Queue, oldIntf) 123 if oldMode != newMode { 124 return false 125 } 126 } 127 return true 128 } 129 130 // Validate validates Rx mode configuration. 131 func (d *RxModeDescriptor) Validate(key string, ifaceWithRxMode *interfaces.Interface) error { 132 for i, rxMode1 := range ifaceWithRxMode.GetRxModes() { 133 if rxMode1.Mode == interfaces.Interface_RxMode_UNKNOWN { 134 if rxMode1.DefaultMode { 135 return kvs.NewInvalidValueError(ErrUndefinedRxMode, "rx_mode[default]") 136 } 137 return kvs.NewInvalidValueError(ErrUndefinedRxMode, 138 fmt.Sprintf("rx_mode[.queue=%d]", rxMode1.Queue)) 139 } 140 for j := i + 1; j < len(ifaceWithRxMode.GetRxModes()); j++ { 141 rxMode2 := ifaceWithRxMode.GetRxModes()[j] 142 if rxMode1.DefaultMode != rxMode2.DefaultMode { 143 continue 144 } 145 if rxMode1.DefaultMode { 146 return kvs.NewInvalidValueError(ErrRedefinedRxMode, "rx_mode[default]") 147 } 148 if rxMode1.Queue == rxMode2.Queue { 149 return kvs.NewInvalidValueError(ErrRedefinedRxMode, 150 fmt.Sprintf("rx_mode[.queue=%d]", rxMode1.Queue)) 151 } 152 } 153 } 154 155 if ifaceWithRxMode.GetType() == interfaces.Interface_DPDK { 156 for _, rxMode := range ifaceWithRxMode.GetRxModes() { 157 mode := normalizeRxMode(rxMode.Mode, ifaceWithRxMode) 158 if mode != interfaces.Interface_RxMode_POLLING { 159 if rxMode.DefaultMode { 160 return kvs.NewInvalidValueError(ErrUnsupportedRxMode, 161 "rx_mode[default]") 162 } 163 return kvs.NewInvalidValueError(ErrUnsupportedRxMode, 164 fmt.Sprintf("rx_mode[.queue=%d]", rxMode.Queue)) 165 } 166 } 167 } 168 return nil 169 } 170 171 // Create configures RxMode for a given interface. 172 // Please note the proto message Interface is only used as container for RxMode. 173 // Only interface name, type and Rx mode are set. 174 func (d *RxModeDescriptor) Create(key string, ifaceWithRxMode *interfaces.Interface) (metadata interface{}, err error) { 175 err = d.configureRxMode(ifaceWithRxMode, kvscheduler.TxnOperation_CREATE) 176 return nil, err 177 } 178 179 // Update modifies Rx mode configuration. 180 func (d *RxModeDescriptor) Update(key string, _, ifaceWithRxMode *interfaces.Interface, 181 oldMetadata interface{}) (newMetadata interface{}, err error) { 182 183 err = d.configureRxMode(ifaceWithRxMode, kvscheduler.TxnOperation_UPDATE) 184 return nil, err 185 } 186 187 // Delete reverts back to the default rx mode configuration. 188 func (d *RxModeDescriptor) Delete(key string, ifaceWithRxMode *interfaces.Interface, metadata interface{}) error { 189 return d.configureRxMode(ifaceWithRxMode, kvscheduler.TxnOperation_DELETE) 190 } 191 192 // configureRxMode (re-)configures Rx mode for the interface. 193 func (d *RxModeDescriptor) configureRxMode(iface *interfaces.Interface, op kvscheduler.TxnOperation) (err error) { 194 195 ifMeta, found := d.ifIndex.LookupByName(iface.Name) 196 if !found { 197 err = errors.Errorf("failed to find interface %s", iface.Name) 198 d.log.Error(err) 199 return err 200 } 201 ifIdx := ifMeta.SwIfIndex 202 203 defRxMode := getDefaultRxMode(iface) 204 205 // first, revert back to default for all queues 206 revertToDefault := op == kvscheduler.TxnOperation_DELETE || 207 (op == kvscheduler.TxnOperation_UPDATE && defRxMode == interfaces.Interface_RxMode_UNKNOWN) 208 if revertToDefault { 209 err = d.ifHandler.SetRxMode(ifIdx, &interfaces.Interface_RxMode{ 210 DefaultMode: true, 211 Mode: normalizeRxMode(interfaces.Interface_RxMode_DEFAULT, iface), 212 }) 213 if err != nil { 214 // treat error as warning here 215 d.log.Warnf("failed to un-configure Rx-mode (%v) - most likely "+ 216 "the interface is already without a link", err) 217 err = nil 218 } 219 } 220 221 if op == kvscheduler.TxnOperation_DELETE { 222 return 223 } 224 225 // configure the requested default Rx mode 226 if defRxMode != interfaces.Interface_RxMode_UNKNOWN { 227 err = d.ifHandler.SetRxMode(ifIdx, &interfaces.Interface_RxMode{ 228 DefaultMode: true, 229 Mode: defRxMode, 230 }) 231 if err != nil { 232 err = errors.Errorf("failed to set default Rx-mode for interface %s: %v", iface.Name, err) 233 d.log.Error(err) 234 return err 235 } 236 } 237 238 // configure per-queue RX mode 239 for _, rxMode := range iface.GetRxModes() { 240 if rxMode.DefaultMode || rxMode.Mode == defRxMode { 241 continue 242 } 243 err = d.ifHandler.SetRxMode(ifIdx, rxMode) 244 if err != nil { 245 err = errors.Errorf("failed to set Rx-mode for queue %d of the interface %s: %v", 246 rxMode.Queue, iface.Name, err) 247 d.log.Error(err) 248 return err 249 } 250 } 251 252 return nil 253 } 254 255 // Dependencies informs scheduler that Rx mode configuration cannot be applied 256 // until the interface link is UP. 257 func (d *RxModeDescriptor) Dependencies(key string, ifaceWithRxMode *interfaces.Interface) (deps []kvs.Dependency) { 258 return []kvs.Dependency{ 259 { 260 Label: linkIsUpDep, 261 Key: interfaces.LinkStateKey(ifaceWithRxMode.Name, true), 262 }, 263 } 264 } 265 266 // getDefaultRxMode reads default RX mode from the interface configuration. 267 func getDefaultRxMode(iface *interfaces.Interface) (rxMode interfaces.Interface_RxMode_Type) { 268 for _, rxMode := range iface.GetRxModes() { 269 if rxMode.DefaultMode { 270 return normalizeRxMode(rxMode.Mode, iface) 271 } 272 } 273 return interfaces.Interface_RxMode_UNKNOWN 274 } 275 276 // getQueueRxMode reads RX mode for the given queue from the interface configuration. 277 func getQueueRxMode(queue uint32, iface *interfaces.Interface) (mode interfaces.Interface_RxMode_Type) { 278 for _, rxMode := range iface.GetRxModes() { 279 if rxMode.DefaultMode { 280 mode = rxMode.Mode 281 continue // keep looking for a queue-specific RX mode 282 } 283 if rxMode.Queue == queue { 284 mode = rxMode.Mode 285 break 286 } 287 } 288 return normalizeRxMode(mode, iface) 289 } 290 291 // normalizeRxMode resolves default/undefined Rx mode for specific interfaces. 292 func normalizeRxMode(mode interfaces.Interface_RxMode_Type, iface *interfaces.Interface) interfaces.Interface_RxMode_Type { 293 if mode != interfaces.Interface_RxMode_DEFAULT { 294 return mode 295 } 296 switch iface.GetType() { 297 case interfaces.Interface_DPDK: 298 return interfaces.Interface_RxMode_POLLING 299 case interfaces.Interface_AF_PACKET: 300 return interfaces.Interface_RxMode_INTERRUPT 301 case interfaces.Interface_TAP: 302 if iface.GetTap().GetVersion() == 2 { 303 // TAP v2 304 return interfaces.Interface_RxMode_INTERRUPT 305 } 306 } 307 return mode 308 }