mirror of
https://github.com/kata-containers/kata-containers.git
synced 2026-07-01 06:28:11 +00:00
runtime: plumb block discard unmap
Pass block-device discard support through the Go QEMU stack. Block drives can now carry a DiscardUnmap request into govmm. QEMU command-line and QMP hotplug paths set discard=unmap on the backend and enable discard on virtio-blk frontends, while leaving SCSI frontend arguments unchanged. Signed-off-by: Manuel Huber <manuelh@nvidia.com> Assisted-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
@@ -1378,6 +1378,9 @@ type BlockDevice struct {
|
||||
// ReadOnly sets the block device in readonly mode
|
||||
ReadOnly bool
|
||||
|
||||
// DiscardUnmap enables discard/unmap support for this block device.
|
||||
DiscardUnmap bool
|
||||
|
||||
// Transport is the virtio transport for this device.
|
||||
Transport VirtioTransport
|
||||
}
|
||||
@@ -1425,6 +1428,9 @@ func (blkdev BlockDevice) QemuParams(config *Config) []string {
|
||||
if blkdev.ShareRW {
|
||||
deviceParams = append(deviceParams, "share-rw=on")
|
||||
}
|
||||
if blkdev.DiscardUnmap {
|
||||
deviceParams = append(deviceParams, "discard=on")
|
||||
}
|
||||
|
||||
deviceParams = append(deviceParams, fmt.Sprintf("serial=%s", blkdev.ID))
|
||||
|
||||
@@ -1437,6 +1443,9 @@ func (blkdev BlockDevice) QemuParams(config *Config) []string {
|
||||
if blkdev.ReadOnly {
|
||||
blkParams = append(blkParams, "readonly=on")
|
||||
}
|
||||
if blkdev.DiscardUnmap {
|
||||
blkParams = append(blkParams, "discard=unmap")
|
||||
}
|
||||
|
||||
qemuParams = append(qemuParams, "-device")
|
||||
qemuParams = append(qemuParams, strings.Join(deviceParams, ","))
|
||||
|
||||
@@ -342,6 +342,29 @@ func TestAppendDeviceBlock(t *testing.T) {
|
||||
testAppend(blkdev, deviceBlockString, t)
|
||||
}
|
||||
|
||||
func TestAppendDeviceBlockDiscardUnmap(t *testing.T) {
|
||||
blkdev := BlockDevice{
|
||||
Driver: VirtioBlock,
|
||||
ID: "hd0",
|
||||
File: "/var/lib/vm.img",
|
||||
AIO: Threads,
|
||||
Format: QCOW2,
|
||||
Interface: NoInterface,
|
||||
WCE: false,
|
||||
DisableModern: true,
|
||||
ROMFile: romfile,
|
||||
ShareRW: true,
|
||||
ReadOnly: true,
|
||||
DiscardUnmap: true,
|
||||
}
|
||||
params := strings.Join(blkdev.QemuParams(&Config{}), " ")
|
||||
for _, opt := range []string{"discard=on", "discard=unmap"} {
|
||||
if !strings.Contains(params, opt) {
|
||||
t.Fatalf("missing %s in block device params: %s", opt, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendDeviceVFIO(t *testing.T) {
|
||||
vfioDevice := VFIODevice{
|
||||
BDF: "02:10.0",
|
||||
|
||||
@@ -800,6 +800,10 @@ func (q *QMP) blockdevAddBaseArgs(driver string, blockDevice *BlockDevice) map[s
|
||||
}
|
||||
|
||||
blockdevArgs["node-name"] = blockDevice.ID
|
||||
if blockDevice.DiscardUnmap {
|
||||
blockdevArgs["discard"] = "unmap"
|
||||
blockdevArgs["file"].(map[string]interface{})["discard"] = "unmap"
|
||||
}
|
||||
|
||||
return blockdevArgs
|
||||
}
|
||||
@@ -863,6 +867,16 @@ func (q *QMP) ExecuteBlockdevAddWithDriverCache(ctx context.Context, driver stri
|
||||
// the logical and physical block sizes for the device; if either is 0, the
|
||||
// hypervisor default is used for that size.
|
||||
func (q *QMP) ExecuteDeviceAdd(ctx context.Context, blockdevID, devID, driver, bus, romfile string, shared, disableModern bool, logicalBlockSize, physicalBlockSize uint32) error {
|
||||
return q.executeDeviceAdd(ctx, blockdevID, devID, driver, bus, romfile, shared, disableModern, false, logicalBlockSize, physicalBlockSize)
|
||||
}
|
||||
|
||||
// ExecuteDeviceAddWithDiscard is like ExecuteDeviceAdd, with explicit virtio-blk
|
||||
// discard support.
|
||||
func (q *QMP) ExecuteDeviceAddWithDiscard(ctx context.Context, blockdevID, devID, driver, bus, romfile string, shared, disableModern, discardUnmap bool, logicalBlockSize, physicalBlockSize uint32) error {
|
||||
return q.executeDeviceAdd(ctx, blockdevID, devID, driver, bus, romfile, shared, disableModern, discardUnmap, logicalBlockSize, physicalBlockSize)
|
||||
}
|
||||
|
||||
func (q *QMP) executeDeviceAdd(ctx context.Context, blockdevID, devID, driver, bus, romfile string, shared, disableModern, discardUnmap bool, logicalBlockSize, physicalBlockSize uint32) error {
|
||||
args := map[string]interface{}{
|
||||
"id": devID,
|
||||
"driver": driver,
|
||||
@@ -880,6 +894,9 @@ func (q *QMP) ExecuteDeviceAdd(ctx context.Context, blockdevID, devID, driver, b
|
||||
if shared {
|
||||
args["share-rw"] = true
|
||||
}
|
||||
if discardUnmap && strings.HasPrefix(driver, "virtio-blk") {
|
||||
args["discard"] = true
|
||||
}
|
||||
if transport.isVirtioPCI(nil) {
|
||||
args["romfile"] = romfile
|
||||
|
||||
@@ -1121,6 +1138,16 @@ func (q *QMP) ExecuteDeviceDel(ctx context.Context, devID string) error {
|
||||
// 1.0 in nested environments. logicalBlockSize and physicalBlockSize specify the logical and
|
||||
// physical sector sizes reported to the guest; set to 0 to use the hypervisor default.
|
||||
func (q *QMP) ExecutePCIDeviceAdd(ctx context.Context, blockdevID, devID, driver, addr, bus, romfile string, queues int, shared, disableModern bool, iothreadID string, logicalBlockSize, physicalBlockSize uint32) error {
|
||||
return q.executePCIDeviceAdd(ctx, blockdevID, devID, driver, addr, bus, romfile, queues, shared, disableModern, false, iothreadID, logicalBlockSize, physicalBlockSize)
|
||||
}
|
||||
|
||||
// ExecutePCIDeviceAddWithDiscard is like ExecutePCIDeviceAdd, with explicit
|
||||
// virtio-blk discard support.
|
||||
func (q *QMP) ExecutePCIDeviceAddWithDiscard(ctx context.Context, blockdevID, devID, driver, addr, bus, romfile string, queues int, shared, disableModern, discardUnmap bool, iothreadID string, logicalBlockSize, physicalBlockSize uint32) error {
|
||||
return q.executePCIDeviceAdd(ctx, blockdevID, devID, driver, addr, bus, romfile, queues, shared, disableModern, discardUnmap, iothreadID, logicalBlockSize, physicalBlockSize)
|
||||
}
|
||||
|
||||
func (q *QMP) executePCIDeviceAdd(ctx context.Context, blockdevID, devID, driver, addr, bus, romfile string, queues int, shared, disableModern, discardUnmap bool, iothreadID string, logicalBlockSize, physicalBlockSize uint32) error {
|
||||
args := map[string]interface{}{
|
||||
"id": devID,
|
||||
"driver": driver,
|
||||
@@ -1133,6 +1160,9 @@ func (q *QMP) ExecutePCIDeviceAdd(ctx context.Context, blockdevID, devID, driver
|
||||
if shared {
|
||||
args["share-rw"] = true
|
||||
}
|
||||
if discardUnmap && strings.HasPrefix(driver, "virtio-blk") {
|
||||
args["discard"] = true
|
||||
}
|
||||
if queues > 0 {
|
||||
args["num-queues"] = queues
|
||||
}
|
||||
|
||||
@@ -508,6 +508,25 @@ func TestQMPBlockdevAddWithCache(t *testing.T) {
|
||||
<-disconnectedCh
|
||||
}
|
||||
|
||||
func TestQMPBlockdevAddDiscardUnmapArgs(t *testing.T) {
|
||||
q := &QMP{}
|
||||
dev := BlockDevice{
|
||||
ID: fmt.Sprintf("drive_%s", volumeUUID),
|
||||
File: "/tmp/disk.img",
|
||||
AIO: Native,
|
||||
DiscardUnmap: true,
|
||||
}
|
||||
|
||||
args := q.blockdevAddBaseArgs("file", &dev)
|
||||
if got := args["discard"]; got != "unmap" {
|
||||
t.Fatalf("unexpected discard option: got %v, expecting unmap", got)
|
||||
}
|
||||
fileArgs := args["file"].(map[string]interface{})
|
||||
if got := fileArgs["discard"]; got != "unmap" {
|
||||
t.Fatalf("unexpected file discard option: got %v, expecting unmap", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that the netdev_add command is correctly sent.
|
||||
//
|
||||
// We start a QMPLoop, send the netdev_add command and stop the loop.
|
||||
|
||||
@@ -2097,10 +2097,11 @@ func (q *qemu) hotplugAddBlockDevice(ctx context.Context, drive *config.BlockDri
|
||||
}
|
||||
|
||||
qblkDevice := govmmQemu.BlockDevice{
|
||||
ID: drive.ID,
|
||||
File: drive.File,
|
||||
ReadOnly: drive.ReadOnly,
|
||||
AIO: govmmQemu.BlockDeviceAIO(q.config.BlockDeviceAIO),
|
||||
ID: drive.ID,
|
||||
File: drive.File,
|
||||
ReadOnly: drive.ReadOnly,
|
||||
DiscardUnmap: drive.DiscardUnmap,
|
||||
AIO: govmmQemu.BlockDeviceAIO(q.config.BlockDeviceAIO),
|
||||
}
|
||||
|
||||
if drive.Swap {
|
||||
@@ -2157,7 +2158,7 @@ func (q *qemu) hotplugAddBlockDevice(ctx context.Context, drive *config.BlockDri
|
||||
iothreadID = fmt.Sprintf("%s_%d", indepIOThreadsPrefix, 0)
|
||||
}
|
||||
|
||||
if err = q.qmpMonitorCh.qmp.ExecutePCIDeviceAdd(q.qmpMonitorCh.ctx, drive.ID, devID, driver, addr, bridge.ID, romFile, queues, true, defaultDisableModern, iothreadID, q.config.BlockDeviceLogicalSectorSize, q.config.BlockDevicePhysicalSectorSize); err != nil {
|
||||
if err = q.qmpMonitorCh.qmp.ExecutePCIDeviceAddWithDiscard(q.qmpMonitorCh.ctx, drive.ID, devID, driver, addr, bridge.ID, romFile, queues, true, defaultDisableModern, drive.DiscardUnmap, iothreadID, q.config.BlockDeviceLogicalSectorSize, q.config.BlockDevicePhysicalSectorSize); err != nil {
|
||||
return err
|
||||
}
|
||||
case q.config.BlockDeviceDriver == config.VirtioBlockCCW:
|
||||
@@ -2176,7 +2177,7 @@ func (q *qemu) hotplugAddBlockDevice(ctx context.Context, drive *config.BlockDri
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = q.qmpMonitorCh.qmp.ExecuteDeviceAdd(q.qmpMonitorCh.ctx, drive.ID, devID, driver, devNoHotplug, "", true, false, q.config.BlockDeviceLogicalSectorSize, q.config.BlockDevicePhysicalSectorSize); err != nil {
|
||||
if err = q.qmpMonitorCh.qmp.ExecuteDeviceAddWithDiscard(q.qmpMonitorCh.ctx, drive.ID, devID, driver, devNoHotplug, "", true, false, drive.DiscardUnmap, q.config.BlockDeviceLogicalSectorSize, q.config.BlockDevicePhysicalSectorSize); err != nil {
|
||||
return err
|
||||
}
|
||||
case q.config.BlockDeviceDriver == config.VirtioSCSI:
|
||||
|
||||
@@ -692,6 +692,7 @@ func genericBlockDevice(drive config.BlockDrive, nestedRun bool) (govmmQemu.Bloc
|
||||
DisableModern: nestedRun,
|
||||
ShareRW: drive.ShareRW,
|
||||
ReadOnly: drive.ReadOnly,
|
||||
DiscardUnmap: drive.DiscardUnmap,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -526,6 +526,21 @@ func TestQemuArchBaseAppendBlockDevice(t *testing.T) {
|
||||
testQemuArchBaseAppend(t, drive, expectedOut)
|
||||
}
|
||||
|
||||
func TestGenericBlockDeviceDiscardUnmap(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
drive := config.BlockDrive{
|
||||
File: "/root",
|
||||
Format: "raw",
|
||||
ID: "blockDevTest",
|
||||
DiscardUnmap: true,
|
||||
}
|
||||
|
||||
blockDevice, err := genericBlockDevice(drive, false)
|
||||
assert.NoError(err)
|
||||
assert.True(blockDevice.DiscardUnmap)
|
||||
}
|
||||
|
||||
func TestQemuArchBaseAppendVhostUserDevice(t *testing.T) {
|
||||
socketPath := "nonexistentpath.sock"
|
||||
macAddress := "00:11:22:33:44:55:66"
|
||||
|
||||
Reference in New Issue
Block a user