...

Source file src/github.com/cilium/ebpf/internal/feature.go

Documentation: github.com/cilium/ebpf/internal

     1  package internal
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  )
     8  
     9  // ErrNotSupported indicates that a feature is not supported by the current kernel.
    10  var ErrNotSupported = errors.New("not supported")
    11  
    12  // UnsupportedFeatureError is returned by FeatureTest() functions.
    13  type UnsupportedFeatureError struct {
    14  	// The minimum Linux mainline version required for this feature.
    15  	// Used for the error string, and for sanity checking during testing.
    16  	MinimumVersion Version
    17  
    18  	// The name of the feature that isn't supported.
    19  	Name string
    20  }
    21  
    22  func (ufe *UnsupportedFeatureError) Error() string {
    23  	if ufe.MinimumVersion.Unspecified() {
    24  		return fmt.Sprintf("%s not supported", ufe.Name)
    25  	}
    26  	return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion)
    27  }
    28  
    29  // Is indicates that UnsupportedFeatureError is ErrNotSupported.
    30  func (ufe *UnsupportedFeatureError) Is(target error) bool {
    31  	return target == ErrNotSupported
    32  }
    33  
    34  type featureTest struct {
    35  	sync.RWMutex
    36  	successful bool
    37  	result     error
    38  }
    39  
    40  // FeatureTestFn is used to determine whether the kernel supports
    41  // a certain feature.
    42  //
    43  // The return values have the following semantics:
    44  //
    45  //   err == ErrNotSupported: the feature is not available
    46  //   err == nil: the feature is available
    47  //   err != nil: the test couldn't be executed
    48  type FeatureTestFn func() error
    49  
    50  // FeatureTest wraps a function so that it is run at most once.
    51  //
    52  // name should identify the tested feature, while version must be in the
    53  // form Major.Minor[.Patch].
    54  //
    55  // Returns an error wrapping ErrNotSupported if the feature is not supported.
    56  func FeatureTest(name, version string, fn FeatureTestFn) func() error {
    57  	ft := new(featureTest)
    58  	return func() error {
    59  		ft.RLock()
    60  		if ft.successful {
    61  			defer ft.RUnlock()
    62  			return ft.result
    63  		}
    64  		ft.RUnlock()
    65  		ft.Lock()
    66  		defer ft.Unlock()
    67  		// check one more time on the off
    68  		// chance that two go routines
    69  		// were able to call into the write
    70  		// lock
    71  		if ft.successful {
    72  			return ft.result
    73  		}
    74  		err := fn()
    75  		switch {
    76  		case errors.Is(err, ErrNotSupported):
    77  			v, err := NewVersion(version)
    78  			if err != nil {
    79  				return err
    80  			}
    81  
    82  			ft.result = &UnsupportedFeatureError{
    83  				MinimumVersion: v,
    84  				Name:           name,
    85  			}
    86  			fallthrough
    87  
    88  		case err == nil:
    89  			ft.successful = true
    90  
    91  		default:
    92  			// We couldn't execute the feature test to a point
    93  			// where it could make a determination.
    94  			// Don't cache the result, just return it.
    95  			return fmt.Errorf("detect support for %s: %w", name, err)
    96  		}
    97  
    98  		return ft.result
    99  	}
   100  }
   101  

View as plain text