summaryrefslogtreecommitdiff
path: root/vendor/github.com/cilium/ebpf/internal/feature.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/cilium/ebpf/internal/feature.go')
-rw-r--r--vendor/github.com/cilium/ebpf/internal/feature.go138
1 files changed, 138 insertions, 0 deletions
diff --git a/vendor/github.com/cilium/ebpf/internal/feature.go b/vendor/github.com/cilium/ebpf/internal/feature.go
new file mode 100644
index 000000000..ec62ed39b
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/internal/feature.go
@@ -0,0 +1,138 @@
+package internal
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+)
+
+// ErrNotSupported indicates that a feature is not supported by the current kernel.
+var ErrNotSupported = errors.New("not supported")
+
+// UnsupportedFeatureError is returned by FeatureTest() functions.
+type UnsupportedFeatureError struct {
+ // The minimum Linux mainline version required for this feature.
+ // Used for the error string, and for sanity checking during testing.
+ MinimumVersion Version
+
+ // The name of the feature that isn't supported.
+ Name string
+}
+
+func (ufe *UnsupportedFeatureError) Error() string {
+ if ufe.MinimumVersion.Unspecified() {
+ return fmt.Sprintf("%s not supported", ufe.Name)
+ }
+ return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion)
+}
+
+// Is indicates that UnsupportedFeatureError is ErrNotSupported.
+func (ufe *UnsupportedFeatureError) Is(target error) bool {
+ return target == ErrNotSupported
+}
+
+type featureTest struct {
+ sync.RWMutex
+ successful bool
+ result error
+}
+
+// FeatureTestFn is used to determine whether the kernel supports
+// a certain feature.
+//
+// The return values have the following semantics:
+//
+// err == ErrNotSupported: the feature is not available
+// err == nil: the feature is available
+// err != nil: the test couldn't be executed
+type FeatureTestFn func() error
+
+// FeatureTest wraps a function so that it is run at most once.
+//
+// name should identify the tested feature, while version must be in the
+// form Major.Minor[.Patch].
+//
+// Returns an error wrapping ErrNotSupported if the feature is not supported.
+func FeatureTest(name, version string, fn FeatureTestFn) func() error {
+ v, err := NewVersion(version)
+ if err != nil {
+ return func() error { return err }
+ }
+
+ ft := new(featureTest)
+ return func() error {
+ ft.RLock()
+ if ft.successful {
+ defer ft.RUnlock()
+ return ft.result
+ }
+ ft.RUnlock()
+ ft.Lock()
+ defer ft.Unlock()
+ // check one more time on the off
+ // chance that two go routines
+ // were able to call into the write
+ // lock
+ if ft.successful {
+ return ft.result
+ }
+ err := fn()
+ switch {
+ case errors.Is(err, ErrNotSupported):
+ ft.result = &UnsupportedFeatureError{
+ MinimumVersion: v,
+ Name: name,
+ }
+ fallthrough
+
+ case err == nil:
+ ft.successful = true
+
+ default:
+ // We couldn't execute the feature test to a point
+ // where it could make a determination.
+ // Don't cache the result, just return it.
+ return fmt.Errorf("detect support for %s: %w", name, err)
+ }
+
+ return ft.result
+ }
+}
+
+// A Version in the form Major.Minor.Patch.
+type Version [3]uint16
+
+// NewVersion creates a version from a string like "Major.Minor.Patch".
+//
+// Patch is optional.
+func NewVersion(ver string) (Version, error) {
+ var major, minor, patch uint16
+ n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch)
+ if n < 2 {
+ return Version{}, fmt.Errorf("invalid version: %s", ver)
+ }
+ return Version{major, minor, patch}, nil
+}
+
+func (v Version) String() string {
+ if v[2] == 0 {
+ return fmt.Sprintf("v%d.%d", v[0], v[1])
+ }
+ return fmt.Sprintf("v%d.%d.%d", v[0], v[1], v[2])
+}
+
+// Less returns true if the version is less than another version.
+func (v Version) Less(other Version) bool {
+ for i, a := range v {
+ if a == other[i] {
+ continue
+ }
+ return a < other[i]
+ }
+ return false
+}
+
+// Unspecified returns true if the version is all zero.
+func (v Version) Unspecified() bool {
+ return v[0] == 0 && v[1] == 0 && v[2] == 0
+}