qemu: Respect the JSON schema for hot plug

When hot-plugging CPUs on QEMU, we send a QMP command with JSON
arguments. QEMU 9.2 recently became more strict[1] enforcing the
JSON schema for QMP parameters. As a result, running Kata Containers
with QEMU 9.2 results in a message complaining that the core-id
parameter is expected to be an integer:

```
qmp hotplug cpu, cpuID=cpu-0 socketID=1, error:
QMP command failed:
Invalid parameter type for 'core-id', expected: integer
```

Fix that by changing the core-id, socket-id and thread-id to be
integer values.

[1]: be93fd5372

Fixes: #11633

Signed-off-by: Christophe de Dinechin <dinechin@redhat.com>
This commit is contained in:
Christophe de Dinechin 2025-08-06 18:51:14 +02:00 committed by Christophe de Dinechin
parent 163f04a918
commit ec480dc438
3 changed files with 17 additions and 19 deletions

View File

@ -1257,25 +1257,23 @@ func (q *QMP) isDieIDSupported(driver string) bool {
// node/board the CPU belongs to, coreID is the core number within socket the CPU belongs to, threadID is the
// thread number within core the CPU belongs to. Note that socketID and threadID are not a requirement for
// architecures like ppc64le.
func (q *QMP) ExecuteCPUDeviceAdd(ctx context.Context, driver, cpuID, socketID, dieID, coreID, threadID, romfile string) error {
func (q *QMP) ExecuteCPUDeviceAdd(ctx context.Context, driver, cpuID string, socketID, dieID, coreID, threadID int, romfile string) error {
args := map[string]interface{}{
"driver": driver,
"id": cpuID,
"core-id": coreID,
}
if socketID != "" && isSocketIDSupported(driver) {
if socketID >= 0 && isSocketIDSupported(driver) {
args["socket-id"] = socketID
}
if threadID != "" && isThreadIDSupported(driver) {
if threadID >= 0 && isThreadIDSupported(driver) {
args["thread-id"] = threadID
}
if q.isDieIDSupported(driver) {
if dieID != "" {
args["die-id"] = dieID
}
if dieID >= 0 && q.isDieIDSupported(driver) {
args["die-id"] = dieID
}
return q.executeCommand(ctx, "device_add", args, nil)

View File

@ -1140,10 +1140,10 @@ func TestQMPAPVFIOMediatedDeviceAdd(t *testing.T) {
func TestQMPCPUDeviceAdd(t *testing.T) {
drivers := []string{"host-x86_64-cpu", "host-s390x-cpu", "host-powerpc64-cpu"}
cpuID := "cpu-0"
socketID := "0"
dieID := "0"
coreID := "1"
threadID := "0"
socketID := 0
dieID := 0
coreID := 1
threadID := 0
for _, d := range drivers {
connectedCh := make(chan *QMPVersion)
disconnectedCh := make(chan struct{})

View File

@ -2197,20 +2197,20 @@ func (q *qemu) hotplugAddCPUs(amount uint32) (uint32, error) {
// CPU type, i.e host-x86_64-cpu
driver := hc.Type
cpuID := fmt.Sprintf("cpu-%d", len(q.state.HotpluggedVCPUs))
socketID := fmt.Sprintf("%d", hc.Properties.Socket)
dieID := fmt.Sprintf("%d", hc.Properties.Die)
coreID := fmt.Sprintf("%d", hc.Properties.Core)
threadID := fmt.Sprintf("%d", hc.Properties.Thread)
socketID := hc.Properties.Socket
dieID := hc.Properties.Die
coreID := hc.Properties.Core
threadID := hc.Properties.Thread
// If CPU type is IBM pSeries, Z or arm virt, we do not set socketID and threadID
if machine.Type == "pseries" || machine.Type == QemuCCWVirtio || machine.Type == "virt" {
socketID = ""
threadID = ""
dieID = ""
socketID = -1
threadID = -1
dieID = -1
}
if err := q.qmpMonitorCh.qmp.ExecuteCPUDeviceAdd(q.qmpMonitorCh.ctx, driver, cpuID, socketID, dieID, coreID, threadID, romFile); err != nil {
q.Logger().WithField("hotplug", "cpu").Warnf("qmp hotplug cpu, cpuID=%s socketID=%s, error: %v", cpuID, socketID, err)
q.Logger().WithField("hotplug", "cpu").Warnf("qmp hotplug cpu, cpuID=%s socketID=%d, error: %v", cpuID, socketID, err)
// don't fail, let's try with other CPU
continue
}