The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/8007
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) ===
From e1e86a1d38e28c6c8d95aae6b5feb6cd2bf74dae Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@canonical.com> Date: Thu, 8 Oct 2020 10:57:55 +0200 Subject: [PATCH 1/7] lxd/device/usb: Allow USB devices for VMs This removes the container-only restriction, and allows USB devices for VMs. Signed-off-by: Thomas Hipp <thomas.h...@canonical.com> --- lxd/device/usb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/device/usb.go b/lxd/device/usb.go index 6e5c09aab9..519da70edb 100644 --- a/lxd/device/usb.go +++ b/lxd/device/usb.go @@ -45,7 +45,7 @@ func (d *usb) isRequired() bool { // validateConfig checks the supplied config for correctness. func (d *usb) validateConfig(instConf instance.ConfigReader) error { - if !instanceSupported(instConf.Type(), instancetype.Container) { + if !instanceSupported(instConf.Type(), instancetype.Container, instancetype.VM) { return ErrUnsupportedDevType } From eadab2d3d214010edeaf8c4c04df59caf94858fb Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@canonical.com> Date: Thu, 8 Oct 2020 16:30:22 +0200 Subject: [PATCH 2/7] lxd/device: Add bus and dev number to USBEvent This adds two new public fields BusNum and DevNum to the USBEvent struct. Signed-off-by: Thomas Hipp <thomas.h...@canonical.com> --- lxd/device/device_utils_usb_events.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lxd/device/device_utils_usb_events.go b/lxd/device/device_utils_usb_events.go index a0a2f01e11..18840eafb2 100644 --- a/lxd/device/device_utils_usb_events.go +++ b/lxd/device/device_utils_usb_events.go @@ -26,6 +26,9 @@ type USBEvent struct { Minor uint32 UeventParts []string UeventLen int + + BusNum int + DevNum int } // usbHandlers stores the event handler callbacks for USB events. @@ -106,14 +109,16 @@ func USBNewEvent(action string, vendor string, product string, major string, min return USBEvent{}, err } + busnumInt := 0 + devnumInt := 0 path := devname if devname == "" { - busnumInt, err := strconv.Atoi(busnum) + busnumInt, err = strconv.Atoi(busnum) if err != nil { return USBEvent{}, err } - devnumInt, err := strconv.Atoi(devnum) + devnumInt, err = strconv.Atoi(devnum) if err != nil { return USBEvent{}, err } @@ -133,5 +138,7 @@ func USBNewEvent(action string, vendor string, product string, major string, min uint32(minorInt), ueventParts, ueventLen, + busnumInt, + devnumInt, }, nil } From bf3c02d11b68ee13da5eca32e61a926adf216eff Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@canonical.com> Date: Fri, 9 Oct 2020 16:36:25 +0200 Subject: [PATCH 3/7] lxd/apparmor: Allow USB specific paths This adds some paths to the AppArmor profile which need to be accessible when using USB pass through. Signed-off-by: Thomas Hipp <thomas.h...@canonical.com> --- lxd/apparmor/instance_qemu.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lxd/apparmor/instance_qemu.go b/lxd/apparmor/instance_qemu.go index c529e76ffc..a1183e27e4 100644 --- a/lxd/apparmor/instance_qemu.go +++ b/lxd/apparmor/instance_qemu.go @@ -27,9 +27,13 @@ profile "{{ .name }}" flags=(attach_disconnected,mediate_deleted) { /dev/vhost-net rw, /dev/vhost-vsock rw, /etc/ceph/** r, + /run/udev/data/* r, + /sys/bus/ r, /sys/bus/nd/devices/ r, - /sys/devices/system/node/ r, - /sys/devices/system/node/** r, + /sys/bus/usb/devices/ r, + /sys/bus/usb/devices/** r, + /sys/class/ r, + /sys/devices/** r, /sys/module/vhost/** r, /{,usr/}bin/qemu* mrix, /usr/share/OVMF/OVMF_CODE.fd kr, From 1a811e7e7ca3f82d3b507d7b5462c1ccd3de579a Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@canonical.com> Date: Fri, 9 Oct 2020 16:37:07 +0200 Subject: [PATCH 4/7] lxd/device/config: Add USBDevice to RunConfig Signed-off-by: Thomas Hipp <thomas.h...@canonical.com> --- lxd/device/config/device_runconfig.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lxd/device/config/device_runconfig.go b/lxd/device/config/device_runconfig.go index eba388047a..f68d77b27e 100644 --- a/lxd/device/config/device_runconfig.go +++ b/lxd/device/config/device_runconfig.go @@ -42,4 +42,5 @@ type RunConfig struct { Uevents [][]string // Uevents to inject. PostHooks []func() error // Functions to be run after device attach/detach. GPUDevice []RunConfigItem // GPU device configuration settings. + USBDevice []RunConfigItem // USB device configuration settings. } From e1cd9f34e268837d932c8842dfbe39cfe85962ac Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@canonical.com> Date: Fri, 9 Oct 2020 16:38:01 +0200 Subject: [PATCH 5/7] lxd/device: Handle USB devices for VMs Signed-off-by: Thomas Hipp <thomas.h...@canonical.com> --- lxd/device/usb.go | 57 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/lxd/device/usb.go b/lxd/device/usb.go index 519da70edb..be646839fd 100644 --- a/lxd/device/usb.go +++ b/lxd/device/usb.go @@ -118,6 +118,14 @@ func (d *usb) Register() error { // Start is run when the device is added to the instance. func (d *usb) Start() (*deviceConfig.RunConfig, error) { + if d.inst.Type() == instancetype.VM { + return d.startVM() + } + + return d.startContainer() +} + +func (d *usb) startContainer() (*deviceConfig.RunConfig, error) { usbs, err := d.loadUsb() if err != nil { return nil, err @@ -144,18 +152,48 @@ func (d *usb) Start() (*deviceConfig.RunConfig, error) { return &runConf, nil } +func (d *usb) startVM() (*deviceConfig.RunConfig, error) { + usbs, err := d.loadUsb() + if err != nil { + return nil, err + } + + runConf := deviceConfig.RunConfig{} + runConf.PostHooks = []func() error{d.Register} + + for _, usb := range usbs { + if !usbIsOurDevice(d.config, &usb) { + continue + } + + runConf.USBDevice = append(runConf.USBDevice, []deviceConfig.RunConfigItem{ + {Key: "devName", Value: d.name}, + {Key: "hostBus", Value: fmt.Sprintf("%d", usb.BusNum)}, + {Key: "hostAddr", Value: fmt.Sprintf("%d", usb.DevNum)}, + }...) + } + + if d.isRequired() && len(runConf.Mounts) <= 0 { + return nil, fmt.Errorf("Required USB device not found") + } + + return &runConf, nil +} + // Stop is run when the device is removed from the instance. func (d *usb) Stop() (*deviceConfig.RunConfig, error) { - // Unregister any USB event handlers for this device. - usbUnregisterHandler(d.inst, d.name) - runConf := deviceConfig.RunConfig{ PostHooks: []func() error{d.postStop}, } - err := unixDeviceRemove(d.inst.DevicesPath(), "unix", d.name, "", &runConf) - if err != nil { - return nil, err + if d.inst.Type() == instancetype.Container { + // Unregister any USB event handlers for this device. + usbUnregisterHandler(d.inst, d.name) + + err := unixDeviceRemove(d.inst.DevicesPath(), "unix", d.name, "", &runConf) + if err != nil { + return nil, err + } } return &runConf, nil @@ -246,3 +284,10 @@ func (d *usb) loadRawValues(p string) (map[string]string, error) { return values, nil } + +// CanHotPlug returns true if attached to a container, otherwise false. It also +// returns a list of fields that can be updated without triggering a device remove & add (in this +// case an empty list). +// func (d *usb) CanHotPlug() (bool, []string) { +// return d.inst.Type() == instancetype.Container, []string{} +// } From c1a959117631fd0177c66c28fd1ad7f6cf1dd562 Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@canonical.com> Date: Fri, 9 Oct 2020 16:38:45 +0200 Subject: [PATCH 6/7] lxd/instance/drivers: Add qemuUSBDev template This adds a device template for USB devices. Signed-off-by: Thomas Hipp <thomas.h...@canonical.com> --- lxd/instance/drivers/driver_qemu_templates.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lxd/instance/drivers/driver_qemu_templates.go b/lxd/instance/drivers/driver_qemu_templates.go index c1b383bc6b..f1f77eb231 100644 --- a/lxd/instance/drivers/driver_qemu_templates.go +++ b/lxd/instance/drivers/driver_qemu_templates.go @@ -508,3 +508,12 @@ addr = "{{.devAddr}}" multifunction = "on" {{- end }} `)) + +var qemuUSBDev = template.Must(template.New("qemuUSBDev").Parse(` +# USB host device ("{{.devName}}" device) +[device "dev-lxd_{{.devName}}"] +driver = "usb-host" +bus = "qemu_usb.0" +hostaddr = "{{.hostAddr}}" +hostbus = "{{.hostBus}}" +`)) From b45f0198c757029860340522eef3443e3a71f376 Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@canonical.com> Date: Fri, 9 Oct 2020 16:39:41 +0200 Subject: [PATCH 7/7] lxd/instance/drivers: Add USB devices to qemu config This adds configured USB devices to the qemu config file. Signed-off-by: Thomas Hipp <thomas.h...@canonical.com> --- lxd/instance/drivers/driver_qemu.go | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 0b697cbf58..0d17257419 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -1842,6 +1842,14 @@ func (vm *qemu) generateQemuConfigFile(busName string, devConfs []*deviceConfig. return "", err } } + + // Add USB device. + if len(runConf.USBDevice) > 0 { + err = vm.addUSBDeviceConfig(sb, bus, runConf.USBDevice) + if err != nil { + return "", err + } + } } // Write the agent mount config. @@ -2256,6 +2264,47 @@ func (vm *qemu) addGPUDevConfig(sb *strings.Builder, bus *qemuBus, gpuConfig []d return nil } +func (vm *qemu) addUSBDeviceConfig(sb *strings.Builder, bus *qemuBus, usbConfig []deviceConfig.RunConfigItem) error { + var devName, hostBus, hostAddr string + + for _, usbItem := range usbConfig { + if usbItem.Key == "devName" { + devName = usbItem.Value + } else if usbItem.Key == "hostBus" { + hostBus = usbItem.Value + } else if usbItem.Key == "hostAddr" { + hostAddr = usbItem.Value + } + } + + tplFields := map[string]interface{}{ + "hostBus": hostBus, + "hostAddr": hostAddr, + "devName": devName, + } + + // Add main GPU device in VGA mode to qemu config. + err := qemuUSBDev.Execute(sb, tplFields) + if err != nil { + return err + } + + hostBusInt, err := strconv.Atoi(hostBus) + if err != nil { + return err + } + + hostAddrInt, err := strconv.Atoi(hostAddr) + if err != nil { + return err + } + + // Add path to devPaths. This way, the path will be included in the apparmor profile. + vm.devPaths = append(vm.devPaths, fmt.Sprintf("/dev/bus/usb/%03d/%03d", hostBusInt, hostAddrInt)) + + return nil +} + // pidFilePath returns the path where the qemu process should write its PID. func (vm *qemu) pidFilePath() string { return filepath.Join(vm.LogPath(), "qemu.pid")
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel