From 9b1511caebe83ef0fed8f097d68cfa9e4e573622 Mon Sep 17 00:00:00 2001 From: Christoph Ostarek Date: Tue, 28 Oct 2025 15:47:19 +0100 Subject: [PATCH 1/2] pci: add parent device address the parent device address is important, too (like IOMMU group), when using pci-passthrough as the both devices should be passed through together Signed-off-by: Christoph Ostarek --- pkg/pci/pci.go | 36 ++++++++++++++++++++---------------- pkg/pci/pci_linux.go | 19 +++++++++++++++++++ pkg/pci/pci_linux_test.go | 23 +++++++++++++++++++++++ 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/pkg/pci/pci.go b/pkg/pci/pci.go index 4afdc3f6..93a43fff 100644 --- a/pkg/pci/pci.go +++ b/pkg/pci/pci.go @@ -21,11 +21,13 @@ import ( type Device struct { // The PCI address of the device - Address string `json:"address"` - Vendor *pcidb.Vendor `json:"vendor"` - Product *pcidb.Product `json:"product"` - Revision string `json:"revision"` - Subsystem *pcidb.Product `json:"subsystem"` + Address string `json:"address"` + // The PCI address of the parent device + ParentAddress string `json:"parent_address"` + Vendor *pcidb.Vendor `json:"vendor"` + Product *pcidb.Product `json:"product"` + Revision string `json:"revision"` + Subsystem *pcidb.Product `json:"subsystem"` // optional subvendor/sub-device information Class *pcidb.Class `json:"class"` // optional sub-class for the device @@ -47,15 +49,16 @@ type devIdent struct { } type devMarshallable struct { - Driver string `json:"driver"` - Address string `json:"address"` - Vendor devIdent `json:"vendor"` - Product devIdent `json:"product"` - Revision string `json:"revision"` - Subsystem devIdent `json:"subsystem"` - Class devIdent `json:"class"` - Subclass devIdent `json:"subclass"` - Interface devIdent `json:"programming_interface"` + Driver string `json:"driver"` + Address string `json:"address"` + ParentAddress string `json:"parent_address"` + Vendor devIdent `json:"vendor"` + Product devIdent `json:"product"` + Revision string `json:"revision"` + Subsystem devIdent `json:"subsystem"` + Class devIdent `json:"class"` + Subclass devIdent `json:"subclass"` + Interface devIdent `json:"programming_interface"` } // NOTE(jaypipes) Device has a custom JSON marshaller because we don't want @@ -64,8 +67,9 @@ type devMarshallable struct { // human-readable name of the vendor, product, class, etc. func (d *Device) MarshalJSON() ([]byte, error) { dm := devMarshallable{ - Driver: d.Driver, - Address: d.Address, + Driver: d.Driver, + Address: d.Address, + ParentAddress: d.ParentAddress, Vendor: devIdent{ ID: d.Vendor.ID, Name: d.Vendor.Name, diff --git a/pkg/pci/pci_linux.go b/pkg/pci/pci_linux.go index 879df436..23068a8d 100644 --- a/pkg/pci/pci_linux.go +++ b/pkg/pci/pci_linux.go @@ -106,6 +106,24 @@ func getDeviceIommuGroup(ctx *context.Context, pciAddr *pciaddr.Address) string return filepath.Base(dest) } +func getDeviceParentAddress(ctx *context.Context, pciAddr *pciaddr.Address) string { + paths := linuxpath.New(ctx) + devPath := filepath.Join(paths.SysBusPciDevices, pciAddr.String()) + + dest, err := os.Readlink(devPath) + if err != nil { + return "" + } + + parentAddr := filepath.Base(filepath.Dir(dest)) + + if pciaddr.FromString(parentAddr) == nil { + return "" + } + + return parentAddr +} + func getDeviceDriver(ctx *context.Context, pciAddr *pciaddr.Address) string { paths := linuxpath.New(ctx) driverPath := filepath.Join(paths.SysBusPciDevices, pciAddr.String(), "driver") @@ -342,6 +360,7 @@ func (info *Info) GetDevice(address string) *Device { device.Node = getDeviceNUMANode(info.ctx, pciAddr) } device.Driver = getDeviceDriver(info.ctx, pciAddr) + device.ParentAddress = getDeviceParentAddress(info.ctx, pciAddr) device.IOMMUGroup = getDeviceIommuGroup(info.ctx, pciAddr) return device } diff --git a/pkg/pci/pci_linux_test.go b/pkg/pci/pci_linux_test.go index 254d53ce..8c8c9b2e 100644 --- a/pkg/pci/pci_linux_test.go +++ b/pkg/pci/pci_linux_test.go @@ -24,6 +24,7 @@ import ( type pciTestCase struct { addr string + parentAddr string node int revision string driver string @@ -95,6 +96,28 @@ func TestPCIDeviceRevision(t *testing.T) { } } +// nolint: gocyclo +func TestPCIParent(t *testing.T) { + info := pciTestSetupI7(t) + tCases := []pciTestCase{ + { + addr: "0000:04:00.0", + parentAddr: "0000:00:06.0", + }, + } + for _, tCase := range tCases { + t.Run(fmt.Sprintf("%s (%s)", tCase.addr, tCase.parentAddr), func(t *testing.T) { + dev := info.GetDevice(tCase.addr) + if dev == nil { + t.Fatalf("got nil device for address %q", tCase.addr) + } + if dev.ParentAddress != tCase.parentAddr { + t.Errorf("got parent %q expected %q", dev.ParentAddress, tCase.parentAddr) + } + }) + } +} + // nolint: gocyclo func TestPCIIommuGroup(t *testing.T) { info := pciTestSetupI7(t) From 330084ddaaad9a51fb6a5ed263374fc218dfeb96 Mon Sep 17 00:00:00 2001 From: Christoph Ostarek Date: Mon, 3 Nov 2025 16:34:25 +0100 Subject: [PATCH 2/2] pkg/pci: add IOMMUGroup to be printed out Signed-off-by: Christoph Ostarek --- pkg/pci/pci.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/pci/pci.go b/pkg/pci/pci.go index 93a43fff..c27928ca 100644 --- a/pkg/pci/pci.go +++ b/pkg/pci/pci.go @@ -59,6 +59,7 @@ type devMarshallable struct { Class devIdent `json:"class"` Subclass devIdent `json:"subclass"` Interface devIdent `json:"programming_interface"` + IOMMUGroup string `json:"iommu_group"` } // NOTE(jaypipes) Device has a custom JSON marshaller because we don't want @@ -95,6 +96,7 @@ func (d *Device) MarshalJSON() ([]byte, error) { ID: d.ProgrammingInterface.ID, Name: d.ProgrammingInterface.Name, }, + IOMMUGroup: d.IOMMUGroup, } return json.Marshal(dm) }