diff --git a/src/libs/protocols/protos/types.proto b/src/libs/protocols/protos/types.proto index a60cbbc326..c09a4faff6 100644 --- a/src/libs/protocols/protos/types.proto +++ b/src/libs/protocols/protos/types.proto @@ -39,8 +39,8 @@ message Interface { string hwAddr = 5; // Path for the device (see the pci::Path (Rust) and types.PciPath - // (Go) or ccw::Device (Rust) types for format details, depending on - // architecture) + // (Go) or ccw::Device (Rust) and types.CcwDevice (Go) types for + // format details, depending on architecture) string devicePath = 6; // Type defines the type of interface described by this structure. diff --git a/src/runtime/virtcontainers/endpoint.go b/src/runtime/virtcontainers/endpoint.go index d65cc8cf6a..f04fdae300 100644 --- a/src/runtime/virtcontainers/endpoint.go +++ b/src/runtime/virtcontainers/endpoint.go @@ -20,10 +20,12 @@ type Endpoint interface { HardwareAddr() string Type() EndpointType PciPath() vcTypes.PciPath + CcwDevice() *vcTypes.CcwDevice NetworkPair() *NetworkInterfacePair SetProperties(NetworkInfo) SetPciPath(vcTypes.PciPath) + SetCcwDevice(vcTypes.CcwDevice) Attach(context.Context, *Sandbox) error Detach(ctx context.Context, netNsCreated bool, netNsPath string) error HotAttach(context.Context, *Sandbox) error diff --git a/src/runtime/virtcontainers/ipvlan_endpoint.go b/src/runtime/virtcontainers/ipvlan_endpoint.go index 9ea66af9d1..2d12cb7b6f 100644 --- a/src/runtime/virtcontainers/ipvlan_endpoint.go +++ b/src/runtime/virtcontainers/ipvlan_endpoint.go @@ -22,6 +22,7 @@ var ipvlanTrace = getNetworkTrace(IPVlanEndpointType) type IPVlanEndpoint struct { EndpointType EndpointType PCIPath vcTypes.PciPath + CCWDevice *vcTypes.CcwDevice EndpointProperties NetworkInfo NetPair NetworkInterfacePair RxRateLimiter bool @@ -88,6 +89,16 @@ func (endpoint *IPVlanEndpoint) SetPciPath(pciPath vcTypes.PciPath) { endpoint.PCIPath = pciPath } +// CcwDevice returns the CCW device of the endpoint. +func (endpoint *IPVlanEndpoint) CcwDevice() *vcTypes.CcwDevice { + return endpoint.CCWDevice +} + +// SetCcwDevice sets the CCW device of the endpoint. +func (endpoint *IPVlanEndpoint) SetCcwDevice(ccwDev vcTypes.CcwDevice) { + endpoint.CCWDevice = &ccwDev +} + // NetworkPair returns the network pair of the endpoint. func (endpoint *IPVlanEndpoint) NetworkPair() *NetworkInterfacePair { return &endpoint.NetPair diff --git a/src/runtime/virtcontainers/macvlan_endpoint.go b/src/runtime/virtcontainers/macvlan_endpoint.go index 71a223e257..85929305ad 100644 --- a/src/runtime/virtcontainers/macvlan_endpoint.go +++ b/src/runtime/virtcontainers/macvlan_endpoint.go @@ -22,6 +22,7 @@ var macvlanTrace = getNetworkTrace(MacvlanEndpointType) type MacvlanEndpoint struct { EndpointType EndpointType PCIPath vcTypes.PciPath + CCWDevice *vcTypes.CcwDevice EndpointProperties NetworkInfo NetPair NetworkInterfacePair RxRateLimiter bool @@ -85,6 +86,16 @@ func (endpoint *MacvlanEndpoint) SetPciPath(pciPath vcTypes.PciPath) { endpoint.PCIPath = pciPath } +// CcwDevice returns the CCW device of the endpoint. +func (endpoint *MacvlanEndpoint) CcwDevice() *vcTypes.CcwDevice { + return endpoint.CCWDevice +} + +// SetCcwDevice sets the CCW device of the endpoint. +func (endpoint *MacvlanEndpoint) SetCcwDevice(ccwDev vcTypes.CcwDevice) { + endpoint.CCWDevice = &ccwDev +} + // NetworkPair returns the network pair of the endpoint. func (endpoint *MacvlanEndpoint) NetworkPair() *NetworkInterfacePair { return &endpoint.NetPair diff --git a/src/runtime/virtcontainers/macvtap_endpoint.go b/src/runtime/virtcontainers/macvtap_endpoint.go index 27fee59f0b..8cf085a647 100644 --- a/src/runtime/virtcontainers/macvtap_endpoint.go +++ b/src/runtime/virtcontainers/macvtap_endpoint.go @@ -25,6 +25,7 @@ type MacvtapEndpoint struct { VMFds []*os.File VhostFds []*os.File PCIPath vcTypes.PciPath + CCWDevice *vcTypes.CcwDevice RxRateLimiter bool TxRateLimiter bool } @@ -112,6 +113,16 @@ func (endpoint *MacvtapEndpoint) SetPciPath(pciPath vcTypes.PciPath) { endpoint.PCIPath = pciPath } +// CcwDevice returns the CCW device of the endpoint. +func (endpoint *MacvtapEndpoint) CcwDevice() *vcTypes.CcwDevice { + return endpoint.CCWDevice +} + +// SetCcwDevice sets the CCW device of the endpoint. +func (endpoint *MacvtapEndpoint) SetCcwDevice(ccwDev vcTypes.CcwDevice) { + endpoint.CCWDevice = &ccwDev +} + // NetworkPair returns the network pair of the endpoint. func (endpoint *MacvtapEndpoint) NetworkPair() *NetworkInterfacePair { return nil diff --git a/src/runtime/virtcontainers/physical_endpoint.go b/src/runtime/virtcontainers/physical_endpoint.go index de6c3cc87a..7e88c4a874 100644 --- a/src/runtime/virtcontainers/physical_endpoint.go +++ b/src/runtime/virtcontainers/physical_endpoint.go @@ -34,6 +34,7 @@ type PhysicalEndpoint struct { Driver string VendorDeviceID string PCIPath vcTypes.PciPath + CCWDevice *vcTypes.CcwDevice } // Properties returns the properties of the physical interface. @@ -66,6 +67,16 @@ func (endpoint *PhysicalEndpoint) SetPciPath(pciPath vcTypes.PciPath) { endpoint.PCIPath = pciPath } +// CcwDevice returns the CCW device of the endpoint. +func (endpoint *PhysicalEndpoint) CcwDevice() *vcTypes.CcwDevice { + return endpoint.CCWDevice +} + +// SetCcwDevice sets the CCW device of the endpoint. +func (endpoint *PhysicalEndpoint) SetCcwDevice(ccwDev vcTypes.CcwDevice) { + endpoint.CCWDevice = &ccwDev +} + // SetProperties sets the properties of the physical endpoint. func (endpoint *PhysicalEndpoint) SetProperties(properties NetworkInfo) { endpoint.EndpointProperties = properties diff --git a/src/runtime/virtcontainers/pkg/agent/protocols/types.pb.go b/src/runtime/virtcontainers/pkg/agent/protocols/types.pb.go index 91660c7249..ae81edfed6 100644 --- a/src/runtime/virtcontainers/pkg/agent/protocols/types.pb.go +++ b/src/runtime/virtcontainers/pkg/agent/protocols/types.pb.go @@ -197,8 +197,8 @@ type Interface struct { Mtu uint64 `protobuf:"varint,4,opt,name=mtu,proto3" json:"mtu,omitempty"` HwAddr string `protobuf:"bytes,5,opt,name=hwAddr,proto3" json:"hwAddr,omitempty"` // Path for the device (see the pci::Path (Rust) and types.PciPath - // (Go) or ccw::Device (Rust) types for format details, depending on - // architecture) + // (Go) or ccw::Device (Rust) and types.CcwDevice (Go) types for + // format details, depending on architecture) DevicePath string `protobuf:"bytes,6,opt,name=devicePath,proto3" json:"devicePath,omitempty"` // Type defines the type of interface described by this structure. // The expected values are the one that are defined by the netlink diff --git a/src/runtime/virtcontainers/tap_endpoint.go b/src/runtime/virtcontainers/tap_endpoint.go index 14ebebfece..9f248e48b8 100644 --- a/src/runtime/virtcontainers/tap_endpoint.go +++ b/src/runtime/virtcontainers/tap_endpoint.go @@ -27,6 +27,7 @@ type TapEndpoint struct { EndpointProperties NetworkInfo EndpointType EndpointType PCIPath vcTypes.PciPath + CCWDevice *vcTypes.CcwDevice RxRateLimiter bool TxRateLimiter bool } @@ -66,6 +67,16 @@ func (endpoint *TapEndpoint) NetworkPair() *NetworkInterfacePair { return nil } +// CcwDevice returns the CCW device of the endpoint. +func (endpoint *TapEndpoint) CcwDevice() *vcTypes.CcwDevice { + return endpoint.CCWDevice +} + +// SetCcwDevice sets the CCW device of the endpoint. +func (endpoint *TapEndpoint) SetCcwDevice(ccwDev vcTypes.CcwDevice) { + endpoint.CCWDevice = &ccwDev +} + // SetProperties sets the properties for the endpoint. func (endpoint *TapEndpoint) SetProperties(properties NetworkInfo) { endpoint.EndpointProperties = properties diff --git a/src/runtime/virtcontainers/tuntap_endpoint.go b/src/runtime/virtcontainers/tuntap_endpoint.go index 6c67b02360..08d28cc07d 100644 --- a/src/runtime/virtcontainers/tuntap_endpoint.go +++ b/src/runtime/virtcontainers/tuntap_endpoint.go @@ -26,6 +26,7 @@ var tuntapTrace = getNetworkTrace(TuntapEndpointType) type TuntapEndpoint struct { EndpointType EndpointType PCIPath vcTypes.PciPath + CCWDevice *vcTypes.CcwDevice TuntapInterface TuntapInterface EndpointProperties NetworkInfo NetPair NetworkInterfacePair @@ -63,6 +64,16 @@ func (endpoint *TuntapEndpoint) SetPciPath(pciPath vcTypes.PciPath) { endpoint.PCIPath = pciPath } +// CcwDevice returns the CCW device of the endpoint. +func (endpoint *TuntapEndpoint) CcwDevice() *vcTypes.CcwDevice { + return endpoint.CCWDevice +} + +// SetCcwDevice sets the CCW device of the endpoint. +func (endpoint *TuntapEndpoint) SetCcwDevice(ccwDev vcTypes.CcwDevice) { + endpoint.CCWDevice = &ccwDev +} + // NetworkPair returns the network pair of the endpoint. func (endpoint *TuntapEndpoint) NetworkPair() *NetworkInterfacePair { return &endpoint.NetPair diff --git a/src/runtime/virtcontainers/types/ccw.go b/src/runtime/virtcontainers/types/ccw.go new file mode 100644 index 0000000000..33e5182953 --- /dev/null +++ b/src/runtime/virtcontainers/types/ccw.go @@ -0,0 +1,42 @@ +// Copyright 2025 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package types + +import ( + "fmt" + "strconv" +) + +// CCW bus ID follow the format .. [1, p. 11], where +// - is the channel subsystem ID, which is always 0 from the guest side, but different from +// the host side, e.g. 0xfe for virtio-*-ccw [1, p. 435], +// - is the subchannel set ID, which ranges from 0-3 [2], and +// - is the device number (0000-ffff; leading zeroes can be omitted, +// e.g. 3 instead of 0003). +// +// [1] https://www.ibm.com/docs/en/linuxonibm/pdf/lku4dd04.pdf +// [2] https://qemu.readthedocs.io/en/master/system/s390x/css.html +const subchannelSetMax = 3 + +type CcwDevice struct { + ssid uint8 + devno uint16 +} + +func CcwDeviceFrom(ssid int, devno string) (CcwDevice, error) { + if ssid < 0 || ssid > subchannelSetMax { + return CcwDevice{}, fmt.Errorf("Subchannel set ID %d should be in range [0..%d]", ssid, subchannelSetMax) + } + v, err := strconv.ParseUint(devno, 16, 16) + if err != nil { + return CcwDevice{}, fmt.Errorf("Failed to parse 0x%v as CCW device number: %v", devno, err) + } + return CcwDevice{ssid: uint8(ssid), devno: uint16(v)}, nil +} + +func (dev CcwDevice) String() string { + return fmt.Sprintf("0.%x.%04x", dev.ssid, dev.devno) +} diff --git a/src/runtime/virtcontainers/types/ccw_test.go b/src/runtime/virtcontainers/types/ccw_test.go new file mode 100644 index 0000000000..932fbd214b --- /dev/null +++ b/src/runtime/virtcontainers/types/ccw_test.go @@ -0,0 +1,39 @@ +// Copyright 2025 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package types + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestPciDevice(t *testing.T) { + assert := assert.New(t) + + // Valid devices + dev, err := CcwDeviceFrom(0, "0000") + assert.NoError(err) + assert.Equal(dev, CcwDevice{0, 0}) + assert.Equal(dev.String(), "0.0.0000") + + dev, err = CcwDeviceFrom(3, "ffff") + assert.NoError(err) + assert.Equal(dev, CcwDevice{3, 65535}) + assert.Equal(dev.String(), "0.3.ffff") + + // Invalid devices + _, err = CcwDeviceFrom(4, "0000") + assert.Error(err) + + _, err = CcwDeviceFrom(-1, "0000") + assert.Error(err) + + _, err = CcwDeviceFrom(-1, "10000") + assert.Error(err) + + _, err = CcwDeviceFrom(-1, "NaN") + assert.Error(err) +} diff --git a/src/runtime/virtcontainers/veth_endpoint.go b/src/runtime/virtcontainers/veth_endpoint.go index 566786d4cb..b9491c9e25 100644 --- a/src/runtime/virtcontainers/veth_endpoint.go +++ b/src/runtime/virtcontainers/veth_endpoint.go @@ -22,6 +22,7 @@ var vethTrace = getNetworkTrace(VethEndpointType) type VethEndpoint struct { EndpointType EndpointType PCIPath vcTypes.PciPath + CCWDevice *vcTypes.CcwDevice EndpointProperties NetworkInfo NetPair NetworkInterfacePair RxRateLimiter bool @@ -83,6 +84,16 @@ func (endpoint *VethEndpoint) SetPciPath(pciPath vcTypes.PciPath) { endpoint.PCIPath = pciPath } +// CcwDevice returns the CCW device of the endpoint. +func (endpoint *VethEndpoint) CcwDevice() *vcTypes.CcwDevice { + return endpoint.CCWDevice +} + +// SetCcwDevice sets the CCW device of the endpoint. +func (endpoint *VethEndpoint) SetCcwDevice(ccwDev vcTypes.CcwDevice) { + endpoint.CCWDevice = &ccwDev +} + // NetworkPair returns the network pair of the endpoint. func (endpoint *VethEndpoint) NetworkPair() *NetworkInterfacePair { return &endpoint.NetPair diff --git a/src/runtime/virtcontainers/vfio_endpoint.go b/src/runtime/virtcontainers/vfio_endpoint.go index f21c6ecf99..86fb5a873e 100644 --- a/src/runtime/virtcontainers/vfio_endpoint.go +++ b/src/runtime/virtcontainers/vfio_endpoint.go @@ -18,6 +18,7 @@ type VfioEndpoint struct { EndpointType EndpointType HostBDF string PCIPath vcTypes.PciPath + CCWDevice *vcTypes.CcwDevice Iface NetworkInterface EndpointProperties NetworkInfo } @@ -64,6 +65,16 @@ func (endpoint *VfioEndpoint) SetPciPath(path vcTypes.PciPath) { endpoint.PCIPath = path } +// CcwDevice returns the CCW device of the endpoint. +func (endpoint *VfioEndpoint) CcwDevice() *vcTypes.CcwDevice { + return endpoint.CCWDevice +} + +// SetCcwDevice sets the CCW device of the endpoint. +func (endpoint *VfioEndpoint) SetCcwDevice(ccwDev vcTypes.CcwDevice) { + endpoint.CCWDevice = &ccwDev +} + // Attach for VFIO endpoint func (endpoint *VfioEndpoint) Attach(ctx context.Context, s *Sandbox) error { return fmt.Errorf("attach is unsupported for VFIO endpoint") diff --git a/src/runtime/virtcontainers/vhostuser_endpoint.go b/src/runtime/virtcontainers/vhostuser_endpoint.go index 5b079629db..c2046eece0 100644 --- a/src/runtime/virtcontainers/vhostuser_endpoint.go +++ b/src/runtime/virtcontainers/vhostuser_endpoint.go @@ -37,6 +37,7 @@ type VhostUserEndpoint struct { EndpointProperties NetworkInfo EndpointType EndpointType PCIPath vcTypes.PciPath + CCWDevice *vcTypes.CcwDevice } // Properties returns the properties of the interface. @@ -74,6 +75,16 @@ func (endpoint *VhostUserEndpoint) SetPciPath(pciPath vcTypes.PciPath) { endpoint.PCIPath = pciPath } +// CcwDevice returns the CCW device of the endpoint. +func (endpoint *VhostUserEndpoint) CcwDevice() *vcTypes.CcwDevice { + return endpoint.CCWDevice +} + +// SetCcwDevice sets the CCW device of the endpoint. +func (endpoint *VhostUserEndpoint) SetCcwDevice(ccwDev vcTypes.CcwDevice) { + endpoint.CCWDevice = &ccwDev +} + // NetworkPair returns the network pair of the endpoint. func (endpoint *VhostUserEndpoint) NetworkPair() *NetworkInterfacePair { return nil