Skip to content

Commit

Permalink
Add support to configure vmi disk I/O mode options
Browse files Browse the repository at this point in the history
QEMU supports 3 disk I/O modes:
- threads
- native (kernel async i/o)
- default

(io_uring offers a better performance than traditional async i/o but it
was introduced in a more recent version of QEMU which we don't use yet
for virt-launcher)

This patch allows users to configure the I/O mode that fits most to
their use-case.

Signed-off-by: Daniel Belenky <[email protected]>
  • Loading branch information
Daniel Belenky committed Aug 5, 2020
1 parent ddce8ab commit 00e78e4
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 0 deletions.
4 changes: 4 additions & 0 deletions api/openapi-spec/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -6846,6 +6846,10 @@
"description": "Attach a volume as a floppy to the vmi.",
"$ref": "#/definitions/v1.FloppyTarget"
},
"io": {
"description": "IO specifies which QEMU disk IO mode should be used. Supported values are: native, default, threads.",
"type": "string"
},
"lun": {
"description": "Attach a volume as a LUN to the vmi.",
"$ref": "#/definitions/v1.LunTarget"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1629,6 +1629,15 @@ func validateDisks(field *k8sfield.Path, disks []v1.Disk) []metav1.StatusCause {
})
}

if disk.IO != "" && disk.IO != v1.IODefault && disk.IO != v1.IONative && disk.IO != v1.IOThreads {
field := field.Child("domain", "devices", "disks").Index(idx).Child("io").String()
causes = append(causes, metav1.StatusCause{
Type: metav1.CauseTypeFieldValueNotSupported,
Message: fmt.Sprintf("Disk IO mode for %s is not supported. Supported modes are: native, threads, default.", field),
Field: field,
})
}

// Verify disk and volume name can be a valid container name since disk
// name can become a container name which will fail to schedule if invalid
errs := validation.IsDNS1123Label(disk.Name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2226,6 +2226,23 @@ var _ = Describe("Validating VMICreate Admitter", func() {
Expect(causes[1].Field).To(Equal("fake[1].lun.bus"))
})

It("should reject disks with unsupported I/O modes", func() {
vmi := v1.NewMinimalVMI("testvmi")

vmi.Spec.Domain.Devices.Disks = append(vmi.Spec.Domain.Devices.Disks, v1.Disk{
Name: "testdisk1",
IO: "native",
})
vmi.Spec.Domain.Devices.Disks = append(vmi.Spec.Domain.Devices.Disks, v1.Disk{
Name: "testdisk2",
IO: "unsupported",
})

causes := validateDisks(k8sfield.NewPath("fake"), vmi.Spec.Domain.Devices.Disks)
Expect(len(causes)).To(Equal(1))
Expect(causes[0].Field).To(Equal("fake.domain.devices.disks[1].io"))
})

It("should reject disk with invalid cache mode", func() {
vmi := v1.NewMinimalVMI("testvmi")
vmi.Spec.Domain.Devices.Disks = append(vmi.Spec.Domain.Devices.Disks, v1.Disk{
Expand Down
1 change: 1 addition & 0 deletions pkg/virt-launcher/virtwrap/api/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ func Convert_v1_Disk_To_api_Disk(diskDevice *v1.Disk, disk *Disk, devicePerBus m
disk.Driver = &DiskDriver{
Name: "qemu",
Cache: string(diskDevice.Cache),
IO: string(diskDevice.IO),
}
if numQueues != nil && disk.Target.Bus == "virtio" {
disk.Driver.Queues = numQueues
Expand Down
26 changes: 26 additions & 0 deletions pkg/virt-launcher/virtwrap/api/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,32 @@ var _ = Describe("Converter", func() {
Expect(xml).To(Equal(convertedDisk))
})

It("should set disk I/O mode if requested", func() {
v1Disk := &v1.Disk{
IO: "native",
}
xml := diskToDiskXML(v1Disk)
expectedXML := `<Disk device="" type="">
<source></source>
<target></target>
<driver io="native" name="qemu" type=""></driver>
<alias name="ua-"></alias>
</Disk>`
Expect(xml).To(Equal(expectedXML))
})

It("should not set disk I/O mode if not requested", func() {
v1Disk := &v1.Disk{}
xml := diskToDiskXML(v1Disk)
expectedXML := `<Disk device="" type="">
<source></source>
<target></target>
<driver name="qemu" type=""></driver>
<alias name="ua-"></alias>
</Disk>`
Expect(xml).To(Equal(expectedXML))
})

It("Should omit boot order when not provided", func() {
kubevirtDisk := &v1.Disk{
Name: "mydisk",
Expand Down
7 changes: 7 additions & 0 deletions staging/src/kubevirt.io/client-go/api/v1/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions staging/src/kubevirt.io/client-go/api/v1/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,10 @@ type Disk struct {
// Cache specifies which kvm disk cache mode should be used.
// +optional
Cache DriverCache `json:"cache,omitempty"`
// IO specifies which QEMU disk IO mode should be used.
// Supported values are: native, default, threads.
// +optional
IO DriverIO `json:"io,omitempty"`
// If specified, disk address and its tag will be provided to the guest via config drive metadata
// +optional
Tag string `json:"tag,omitempty"`
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions staging/src/kubevirt.io/client-go/api/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1006,11 +1006,25 @@ const (
// +k8s:openapi-gen=true
type DriverCache string

//
// +k8s:openapi-gen=true
type DriverIO string

const (
// CacheNone - I/O from the guest is not cached on the host, but may be kept in a writeback disk cache.
CacheNone DriverCache = "none"
// CacheWriteThrough - I/O from the guest is cached on the host but written through to the physical medium.
CacheWriteThrough DriverCache = "writethrough"

// IOThreads - User mode based threads with a shared lock that perform I/O tasks. Can impact performance but offers
// more predictable behaviour. This method is also takes fewer CPU cycles to submit I/O requests.
IOThreads DriverIO = "threads"
// IONative - Kernel native I/O tasks (AIO) offer a better performance but can block the VM if the file is not fully
// allocated so this method recommended only when the backing file/disk/etc is fully preallocated.
IONative DriverIO = "native"
// IODefault - Fallback to the default value from the kernel. With recent Kernel versions (for example RHEL-7) the
// default is AIO.
IODefault DriverIO = "default"
)

// Handler defines a specific action that should be taken
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 00e78e4

Please sign in to comment.