Skip to content

Conversation

lmb
Copy link
Collaborator

@lmb lmb commented Feb 17, 2025

Linux and Windows share the concept of program types, map types and so on. For example, XDP is supported on both platforms. Unfortunately the constant values used by both platforms are different. On Linux, XDP has value 0x6 while on Windows it is 0x1.

This problem extends to a CollectionSpec parsed from an ELF. Because of the different values a Windows PerCPUHash aliases with a Linux ProgramArray. This causes all sorts of mayhem that is hard to avoid without making too many changes to the code base.

Introduce the notion of a supported platform. External users only see this as a string, like "linux". Internally each platform has a corresponding tag which is used to encode the platform into a constant. This means that there are distinct constants for each platform. Instead of overloading the XDP constant we will introduce WindowsXDP. This will allow the ELF reader to remain platform independent and makes generating stringers a lot easier.

The value of the platform tag is chosen so that on Linux the constant values do not change and are identical with the value in upstream headers. On Windows the constants have a fixed (but implementation defined) offset.

The downside of this approach is that Windows constants will all require a prefix of some sort to distinguish them from Linux ones. This is probably most onerous for map types, because those tend to be created manually more frequently than programs, for example. We can add some behind the scenes translation of Linux map types to Windows ones if need be.

Another downside is that constant values are now limited to 28 bits since we steal the top 4 bits to store the platform. The constants we're applying this to are all sequentially numbered from 0, so this is hopefully enough.

@github-actions github-actions bot added the breaking-change Changes exported API label Feb 17, 2025
@lmb lmb force-pushed the windows-constants branch from 7379a07 to c38959e Compare February 17, 2025 17:40
@lmb
Copy link
Collaborator Author

lmb commented Feb 17, 2025

github.com/cilium/ebpf/asm
  Incompatible changes:
  - (*Instruction).Unmarshal: changed from func(io.Reader, encoding/binary.ByteOrder) (uint64, error) to func(io.Reader, encoding/binary.ByteOrder, string) error
  - (*Instructions).Unmarshal: removed
  - BuiltinFunc: changed from int32 to uint32

@lmb lmb marked this pull request as ready for review February 24, 2025 09:42
@lmb lmb requested review from mmat11 and a team as code owners February 24, 2025 09:42
@lmb lmb force-pushed the windows-constants branch from c38959e to 3843dfc Compare February 24, 2025 09:43
}

// AppendInstructions decodes [Instruction] from r and appends them to insns.
func AppendInstructions(insns Instructions, r io.Reader, bo binary.ByteOrder, platform string) (Instructions, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any strong reason for this change? Could this become Instructions.Append() instead of a free function? I agree it makes more sense than Unmarshal() with the accompanying replacement semantic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The common operation is to encode into a new slice. With a free function that is:

AppendInstructions(nil, ...)

As a method it requires an extra line:

var insns Instructions
insns.Append(...)

@@ -104,6 +105,16 @@ const (
Arena
)

// MapTypeForPlatform returns a platform specific map type.
func MapTypeForPlatform(plat string, typ uint32) (MapType, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pure personal taste, but I find this a bit unusual API. If I understand correctly, exernal callers can use this to create MapSpecs for a given target based on a well-known map type (e.g. give me whatever Hash Windows supports), but also to check if ebpf-go supports the Windows equivalent of a given map type (e.g. PerCPUCGroupStorage.On(platform.Windows) will fail). This could be useful for creating portable apps if such a thing ever makes sense.

What about making this a method instead? It allows for more succinct/expressive uses:

t, err := HashOfMaps.On(platform.Windows)

(or HashOfMaps.For(platform.Windows))

compared to

t, err := MapTypeForPlatform(platforn.Windows, HashOfMaps)

Initially, I thought of this as t, err := HashOfMaps.Platform(platforn.Windows), but that stutters when used with the internal platform constants.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not how it's intended to be used at the moment. It solves the problem of what to do if a new map type is introduced which the library doesn't have a constant for (yet). Then you can do:

mapType, err := MapTypeForPlatform("windows", 0xfeed)
// use mapType in calls that expect MapType

Remember that HashOfMaps on linux does not have the same numerical value as HashOfMaps on windows.

@lmb lmb force-pushed the windows-constants branch from 3843dfc to 915e269 Compare March 3, 2025 11:24
Linux and Windows share the concept of program types, map types and so on.
For example, XDP is supported on both platforms. Unfortunately the constant
values used by both platforms are different. On Linux, XDP has value 0x6
while on Windows it is 0x1.

This problem extends to a CollectionSpec parsed from an ELF. Because of the
different values a Windows PerCPUHash aliases with a Linux ProgramArray.
This causes all sorts of mayhem that is hard to avoid without making too
many changes to the code base.

Introduce the notion of a supported platform. External users only see
this as a string, like "linux". Internally each platform has a
corresponding tag which is used to encode the platform into a constant.
This means that there are distinct constants for each platform.
Instead of overloading the XDP constant we will introduce WindowsXDP.
This will allow the ELF reader to remain platform independent and
makes generating stringers a lot easier.

The value of the platform tag is chosen so that on Linux the constant
values do not change and are identical with the value in upstream
headers. On Windows the constants have a fixed (but implementation
defined) offset.

The downside of this approach is that Windows constants will all require
a prefix of some sort to distinguish them from Linux ones. This is probably
most onerous for map types, because those tend to be created manually more
frequently than programs, for example. We can add some behind the scenes
translation of Linux map types to Windows ones if need be.

Another downside is that constant values are now limited to 28 bits since
we steal the top 4 bits to store the platform. The constants we're applying
this to are all sequentially numbered from 0, so this is hopefully enough.

Signed-off-by: Lorenz Bauer <lmb@isovalent.com>
@lmb lmb force-pushed the windows-constants branch from 915e269 to 76a7f5a Compare March 3, 2025 11:28
@lmb lmb merged commit 6bba483 into cilium:main Mar 3, 2025
17 checks passed
@lmb lmb deleted the windows-constants branch March 3, 2025 11:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking-change Changes exported API
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants