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  }