From 52bbe7c9a7e6b878b887eac7827ee04b49ff4750 Mon Sep 17 00:00:00 2001 From: wanyaoqi <18528551+wanyaoqi@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:52:49 +0800 Subject: [PATCH] fix(host): usb device passthrough use hostport (#24708) --- pkg/compute/models/isolated_devices.go | 2 +- .../isolated_device/isolated_device.go | 15 +++++ pkg/hostman/isolated_device/usb.go | 66 +++++++++++++++++-- pkg/hostman/isolated_device/usb_test.go | 2 +- 4 files changed, 77 insertions(+), 8 deletions(-) diff --git a/pkg/compute/models/isolated_devices.go b/pkg/compute/models/isolated_devices.go index 2d92253e9f..97750794cb 100644 --- a/pkg/compute/models/isolated_devices.go +++ b/pkg/compute/models/isolated_devices.go @@ -120,7 +120,7 @@ type SIsolatedDevice struct { // guest disk index DiskIndex int8 `nullable:"true" default:"-1" list:"user" update:"user"` - // # pci address of `Bus:Device.Function` format, or usb bus address of `bus.addr` + // # pci address of `Bus:Device.Function` format, or usb bus address of `bus:addr:port` Addr string `width:"16" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"domain_optional"` DevicePath string `width:"128" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"optional"` diff --git a/pkg/hostman/isolated_device/isolated_device.go b/pkg/hostman/isolated_device/isolated_device.go index 6aa55fb8c7..b76bed64e4 100644 --- a/pkg/hostman/isolated_device/isolated_device.go +++ b/pkg/hostman/isolated_device/isolated_device.go @@ -562,6 +562,21 @@ func (man *isolatedDeviceManager) CheckDevIsNeedUpdate(dev IDevice, devInfo *Clo func (man *isolatedDeviceManager) GetDeviceByIdent(vendorDevId, addr, mdevId string) IDevice { for _, dev := range man.devices { + if dev.GetDeviceType() == api.USB_TYPE && dev.GetVendorDeviceId() == vendorDevId { + raddrSplit := strings.Split(addr, ":") + saddrSplit := strings.Split(dev.GetAddr(), ":") + + // first update, same bus and dev + if len(raddrSplit) == 2 && raddrSplit[0] == saddrSplit[0] && raddrSplit[1] == saddrSplit[1] { + return dev + } + + // same bus and port + if len(raddrSplit) == 3 && raddrSplit[0] == saddrSplit[0] && raddrSplit[2] == saddrSplit[2] { + return dev + } + } + if dev.GetVendorDeviceId() == vendorDevId && dev.GetAddr() == addr && dev.GetMdevId() == mdevId { return dev } diff --git a/pkg/hostman/isolated_device/usb.go b/pkg/hostman/isolated_device/usb.go index ac6c0d2971..0730b14eae 100644 --- a/pkg/hostman/isolated_device/usb.go +++ b/pkg/hostman/isolated_device/usb.go @@ -32,6 +32,8 @@ import ( type sUSBDevice struct { *SBaseDevice lsusbLine *sLsusbLine + + PortPath string } // TODO: rename PCIDevice @@ -42,6 +44,13 @@ func newUSBDevice(dev *PCIDevice, lsusbLine *sLsusbLine) *sUSBDevice { } } +func (dev *sUSBDevice) SetPortPath(portPath string) { + dev.PortPath = portPath + if len(dev.PortPath) > 0 { + dev.dev.Addr = fmt.Sprintf("%s:%s", dev.dev.Addr, portPath) + } +} + func (dev *sUSBDevice) GetCPUCmd() string { return "" } @@ -59,7 +68,7 @@ func GetUSBDevId(vendorId, devId, bus, addr string) string { return fmt.Sprintf("dev_%s_%s-%s_%s", vendorId, devId, bus, addr) } -func getUSBDevQemuOptions(vendorId, deviceId string, bus, addr string) (map[string]string, error) { +func getUSBDevQemuOptions(vendorId, deviceId string, bus, addr, port string) (map[string]string, error) { // id := GetUSBDevId(vendorId, deviceId, bus, addr) busI, err := strconv.Atoi(bus) if err != nil { @@ -69,6 +78,13 @@ func getUSBDevQemuOptions(vendorId, deviceId string, bus, addr string) (map[stri if err != nil { return nil, errors.Wrapf(err, "parse addr to int %q", bus) } + if len(port) > 0 { + return map[string]string{ + "hostbus": fmt.Sprintf("%d", busI), + "hostport": port, + }, nil + } + return map[string]string{ // "id": id, // "bus": "usb.0", @@ -88,13 +104,17 @@ func GetUSBDevQemuOptions(vendorDevId string, addr string) (map[string]string, e productId := parts[1] addrParts := strings.Split(addr, ":") - if len(addrParts) != 2 { + if len(addrParts) != 2 && len(addrParts) != 3 { return nil, fmt.Errorf("invalid addr %q", addr) } hostBus := addrParts[0] hostAddr := addrParts[1] + hostPort := "" + if len(addrParts) == 3 { + hostPort = addrParts[2] + } - return getUSBDevQemuOptions(vendorId, productId, hostBus, hostAddr) + return getUSBDevQemuOptions(vendorId, productId, hostBus, hostAddr, hostPort) } func (dev *sUSBDevice) GetKernelDriver() (string, error) { @@ -177,7 +197,7 @@ func getPassthroughUSBs() ([]*sUSBDevice, error) { } // check by trees - isHubClass, err := isUSBHubClass(dev, trees) + isHubClass, err := checkIsUSBHubClassAndSetPortPath(dev, trees) if err != nil { return nil, errors.Wrap(err, "check isUSBHubClass") } @@ -197,7 +217,7 @@ func isUSBLinuxRootHub(vendorId string, deviceId string) bool { return false } -func isUSBHubClass(dev *sUSBDevice, trees *sLsusbTrees) (bool, error) { +func checkIsUSBHubClassAndSetPortPath(dev *sUSBDevice, trees *sLsusbTrees) (bool, error) { busNum, err := dev.lsusbLine.GetBusNumber() if err != nil { return false, errors.Wrapf(err, "GetBusNumber of dev %#v", dev.lsusbLine) @@ -214,8 +234,12 @@ func isUSBHubClass(dev *sUSBDevice, trees *sLsusbTrees) (bool, error) { if treeDev == nil { return false, errors.Errorf("not found dev %#v by bus %d, dev %d", dev.lsusbLine, busNum, devNum) } + if utils.IsInStringArray(treeDev.Class, []string{"root_hub", "Hub"}) { + return true, nil + } - return utils.IsInStringArray(treeDev.Class, []string{"root_hub", "Hub"}), nil + dev.SetPortPath(tree.GetPortPath(devNum)) + return false, nil } func parseLsusb(lines []string) ([]*sUSBDevice, error) { @@ -516,6 +540,36 @@ func (t *sLsusbTree) GetContents() []string { return ret } +func (t *sLsusbTree) GetPortPath(devNum int) string { + portPath, found := t.GetDevicePortPath("", devNum) + if !found { + return "" + } + return portPath +} + +func (t *sLsusbTree) GetDevicePortPath(portPath string, devNum int) (string, bool) { + if !t.IsRootBus { + if len(portPath) > 0 { + portPath = fmt.Sprintf("%s.%d", portPath, t.Port) + } else { + portPath = strconv.Itoa(t.Port) + } + } + + if t.Dev == devNum { + return portPath, true + } + + for _, node := range t.Nodes { + devPortPath, found := node.GetDevicePortPath(portPath, devNum) + if found { + return devPortPath, true + } + } + return "", false +} + func (t *sLsusbTree) GetDevice(devNum int) *sLsusbTree { // should check self firstly if t.Dev == devNum { diff --git a/pkg/hostman/isolated_device/usb_test.go b/pkg/hostman/isolated_device/usb_test.go index c754506393..00ef6f7c02 100644 --- a/pkg/hostman/isolated_device/usb_test.go +++ b/pkg/hostman/isolated_device/usb_test.go @@ -178,7 +178,7 @@ func Test_getUSBDevQemuOptions(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got, _ := getUSBDevQemuOptions(tt.args.vendorId, tt.args.deviceId, tt.args.bus, tt.args.addr); !reflect.DeepEqual(got, tt.want) { + if got, _ := getUSBDevQemuOptions(tt.args.vendorId, tt.args.deviceId, tt.args.bus, tt.args.addr, ""); !reflect.DeepEqual(got, tt.want) { t.Errorf("getUSBDevQemuOptions() = %v, want %v", got, tt.want) } })