diff mbox

[RFC] qmp: query-device-slots command

Message ID 20161208200737.GA29563@thinpad.lan.raisama.net (mailing list archive)
State New, archived
Headers show

Commit Message

Eduardo Habkost Dec. 8, 2016, 8:07 p.m. UTC
This adds a new command to QMP: query-device-slots. It will allow
management software to query possible slots where devices can be
plugged.

This implementation of the command will return:

* Multiple PCI slots per bus, in the case of PCI buses;
* One slot per bus in the case of the other buses;
* One slot for each entry from query-hotpluggable-cpus.

In the example below, I am not sure if the PCIe ports are all
supposed to report all slots, but I didn't find any existing
field in PCIBus that would help me figure out the actual number
of slots in a given PCI bus.

Git tree
--------

This patch needs the previous query-machines series I am working
on. The full tree can be found on the git tree at:

  git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info

Example output
--------------

The following output was returned by QEMU when running it as:

 $ qemu-system-x86_64 -machine q35 \
   -readconfig docs/q35-chipset.cfg \
   -smp 4,maxcpus=8,sockets=2,cores=2,threads=2

 {
    "return": [
        {
            "available": false,
            "devices": [
                "/machine/unattached/device[30]",
                "/machine/unattached/device[29]",
                "/machine/unattached/device[28]",
                "/machine/unattached/device[27]",
                "/machine/unattached/device[26]",
                "/machine/unattached/device[25]",
                "/machine/unattached/device[24]",
                "/machine/unattached/device[23]"
            ],
            "accepted-device-types": [
                "i2c-slave"
            ],
            "props": {
                "bus": "/machine/unattached/device[22]/i2c"
            },
            "hotpluggable": false,
            "type": "generic"
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "ide-device"
            ],
            "props": {
                "bus": "/machine/unattached/device[20]/ide.4"
            },
            "hotpluggable": false,
            "type": "generic"
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "ide-device"
            ],
            "props": {
                "bus": "/machine/unattached/device[20]/ide.5"
            },
            "hotpluggable": false,
            "type": "generic"
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "ide-device"
            ],
            "props": {
                "bus": "/machine/unattached/device[20]/ide.0"
            },
            "hotpluggable": false,
            "type": "generic"
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "ide-device"
            ],
            "props": {
                "bus": "/machine/unattached/device[20]/ide.1"
            },
            "hotpluggable": false,
            "type": "generic"
        },
        {
            "available": false,
            "devices": [
                "/machine/unattached/device[21]"
            ],
            "accepted-device-types": [
                "ide-device"
            ],
            "props": {
                "bus": "/machine/unattached/device[20]/ide.2"
            },
            "hotpluggable": false,
            "type": "generic"
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "ide-device"
            ],
            "props": {
                "bus": "/machine/unattached/device[20]/ide.3"
            },
            "hotpluggable": false,
            "type": "generic"
        },
        {
            "available": false,
            "devices": [
                "/machine/unattached/device[8]",
                "/machine/q35/ioapic",
                "/machine/q35",
                "/machine/fw_cfg",
                "/machine/unattached/device[1]"
            ],
            "accepted-device-types": [
                "sys-bus-device"
            ],
            "props": {
                "bus": "/machine/unattached/sysbus"
            },
            "hotpluggable": false,
            "type": "generic"
        },
        {
            "available": false,
            "devices": [
                "/machine/unattached/device[19]",
                "/machine/unattached/device[18]",
                "/machine/unattached/device[17]",
                "/machine/unattached/device[16]",
                "/machine/unattached/device[15]",
                "/machine/unattached/device[14]",
                "/machine/unattached/device[13]",
                "/machine/unattached/device[12]",
                "/machine/unattached/device[11]",
                "/machine/unattached/device[10]",
                "/machine/unattached/device[9]",
                "/machine/unattached/device[7]",
                "/machine/unattached/device[6]"
            ],
            "accepted-device-types": [
                "isa-device"
            ],
            "props": {
                "bus": "/machine/unattached/device[5]/isa.0"
            },
            "hotpluggable": false,
            "type": "generic"
        },
        {
            "available": false,
            "devices": [
                "/machine/q35/mch"
            ],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 0
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [
                "/machine/unattached/device[31]"
            ],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 8
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [
                "/machine/unattached/device[32]"
            ],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 16
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 24
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 32
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 40
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 48
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 56
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 64
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 72
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 80
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 88
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 96
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 104
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 112
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 120
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 128
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 136
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 144
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 152
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 160
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 168
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 176
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 184
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 192
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 200
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [
                "/machine/peripheral/ich9-uhci-4"
            ],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 208
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [
                "/machine/peripheral/ich9-hda-audio"
            ],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 216
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [
                "/machine/peripheral/ich9-pcie-port-1"
            ],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 224
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [
                "/machine/peripheral/ich9-uhci-1"
            ],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 232
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [
                "/machine/peripheral/ich9-pci-bridge"
            ],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 240
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [
                "/machine/unattached/device[5]"
            ],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 248
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 0
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 8
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 16
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 24
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 32
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 40
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 48
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 56
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 64
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 72
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 80
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 88
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 96
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 104
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 112
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 120
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 128
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 136
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 144
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 152
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 160
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 168
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 176
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 184
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 192
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 200
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 208
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 216
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 224
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 232
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 240
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-1/ich9-pcie-port-1",
                "addr": 248
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 0
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 8
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 16
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 24
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 32
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 40
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 48
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 56
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 64
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 72
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 80
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 88
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 96
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 104
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 112
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 120
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 128
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 136
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 144
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 152
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 160
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 168
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 176
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 184
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 192
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 200
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 208
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 216
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 224
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 232
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 240
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-2/ich9-pcie-port-2",
                "addr": 248
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 0
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 8
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 16
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 24
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 32
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 40
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 48
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 56
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 64
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 72
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 80
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 88
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 96
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 104
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 112
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 120
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 128
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 136
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 144
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 152
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 160
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 168
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 176
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 184
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 192
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 200
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 208
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 216
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 224
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 232
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 240
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 248
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 0
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 8
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 16
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 24
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 32
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 40
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 48
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 56
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 64
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 72
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 80
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 88
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 96
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 104
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 112
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 120
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 128
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 136
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 144
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 152
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 160
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 168
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 176
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 184
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 192
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 200
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 208
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 216
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 224
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 232
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 240
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-4/ich9-pcie-port-4",
                "addr": 248
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 0
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 8
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 16
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 24
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 32
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 40
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 48
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 56
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 64
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 72
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 80
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 88
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 96
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 104
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 112
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 120
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 128
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 136
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 144
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 152
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 160
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 168
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 176
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 184
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 192
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 200
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 208
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 216
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 224
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 232
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 240
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pci-bridge/ich9-pci-bridge",
                "addr": 248
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "usb-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-ehci-1/ich9-ehci-1.0"
            },
            "hotpluggable": true,
            "type": "generic"
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "usb-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-ehci-2/ich9-ehci-2.0"
            },
            "hotpluggable": true,
            "type": "generic"
        },
        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "hda-codec"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-hda-audio/ich9-hda-audio.0"
            },
            "hotpluggable": false,
            "type": "generic"
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "qemu64-x86_64-cpu"
            ],
            "props": {
                "socket-id": 1,
                "core-id": 1,
                "thread-id": 1
            },
            "hotpluggable": true,
            "type": "cpu",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "qemu64-x86_64-cpu"
            ],
            "props": {
                "socket-id": 1,
                "core-id": 1,
                "thread-id": 0
            },
            "hotpluggable": true,
            "type": "cpu",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "qemu64-x86_64-cpu"
            ],
            "props": {
                "socket-id": 1,
                "core-id": 0,
                "thread-id": 1
            },
            "hotpluggable": true,
            "type": "cpu",
            "max-devices": 1
        },
        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "qemu64-x86_64-cpu"
            ],
            "props": {
                "socket-id": 1,
                "core-id": 0,
                "thread-id": 0
            },
            "hotpluggable": true,
            "type": "cpu",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [
                "/machine/unattached/device[4]"
            ],
            "accepted-device-types": [
                "qemu64-x86_64-cpu"
            ],
            "props": {
                "socket-id": 0,
                "core-id": 1,
                "thread-id": 1
            },
            "hotpluggable": true,
            "type": "cpu",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [
                "/machine/unattached/device[3]"
            ],
            "accepted-device-types": [
                "qemu64-x86_64-cpu"
            ],
            "props": {
                "socket-id": 0,
                "core-id": 1,
                "thread-id": 0
            },
            "hotpluggable": true,
            "type": "cpu",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [
                "/machine/unattached/device[2]"
            ],
            "accepted-device-types": [
                "qemu64-x86_64-cpu"
            ],
            "props": {
                "socket-id": 0,
                "core-id": 0,
                "thread-id": 1
            },
            "hotpluggable": true,
            "type": "cpu",
            "max-devices": 1
        },
        {
            "available": false,
            "devices": [
                "/machine/unattached/device[0]"
            ],
            "accepted-device-types": [
                "qemu64-x86_64-cpu"
            ],
            "props": {
                "socket-id": 0,
                "core-id": 0,
                "thread-id": 0
            },
            "hotpluggable": true,
            "type": "cpu",
            "max-devices": 1
        }
    ]
  }

Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
---
 qapi-schema.json       | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/qdev-core.h |   6 +++
 hw/core/bus.c          |  49 +++++++++++++++++++++
 hw/pci/pci.c           | 106 +++++++++++++++++++++++++++++++++------------
 qdev-monitor.c         |  86 ++++++++++++++++++++++++++++++++++---
 5 files changed, 328 insertions(+), 33 deletions(-)

Comments

Markus Armbruster Dec. 13, 2016, 11:04 a.m. UTC | #1
Quick interface review only:

Eduardo Habkost <ehabkost@redhat.com> writes:

> This adds a new command to QMP: query-device-slots. It will allow
> management software to query possible slots where devices can be
> plugged.
>
> This implementation of the command will return:
>
> * Multiple PCI slots per bus, in the case of PCI buses;
> * One slot per bus in the case of the other buses;

Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
or two (PATA).  For more examples, see qdev-device-use.txt section
"Specifying Bus and Address on Bus".

> * One slot for each entry from query-hotpluggable-cpus.
> In the example below, I am not sure if the PCIe ports are all
> supposed to report all slots, but I didn't find any existing
> field in PCIBus that would help me figure out the actual number
> of slots in a given PCI bus.
>
> Git tree
> --------
>
> This patch needs the previous query-machines series I am working
> on. The full tree can be found on the git tree at:
>
>   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
>
> Example output
> --------------
>
> The following output was returned by QEMU when running it as:
>
>  $ qemu-system-x86_64 -machine q35 \
>    -readconfig docs/q35-chipset.cfg \
>    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
>
>  {
>     "return": [

Are you sure >3000 lines of example output make sense here?

[...]
>     ]
>   }
>
> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> ---
>  qapi-schema.json       | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/qdev-core.h |   6 +++
>  hw/core/bus.c          |  49 +++++++++++++++++++++
>  hw/pci/pci.c           | 106 +++++++++++++++++++++++++++++++++------------
>  qdev-monitor.c         |  86 ++++++++++++++++++++++++++++++++++---
>  5 files changed, 328 insertions(+), 33 deletions(-)
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index d48ff3f..484d91e 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -3166,6 +3166,120 @@
>  { 'command': 'closefd', 'data': {'fdname': 'str'} }
>  
>  ##
> +# @DeviceSlotType:
> +#
> +# Type of device slot
> +#
> +# @generic: Generic device slot, with no bus-specific information
> +# @pci: PCI device slot
> +# @cpu: CPU device slot
> +##
> +{ 'enum': 'DeviceSlotType',
> +  'data': ['generic', 'pci', 'cpu'] }
> +
> +##
> +# @DeviceSlotInfo:
> +#
> +# Information on a slot where devices can be plugged. Some buses
> +# are represented as a single slot that can support multiple devices,
> +# and some buses have multiple slots that are identified by arguments
> +# to @device_add.

Okay, let me try to wrap my poor, ignorant mind around this.

There are two kinds of buses: "single slot that can support multiple
devices", and "multiple slots that are identified by arguments".

How are the two kinds related to @type?

Examples for "multiple slots that are identified by arguments":

    -device edu,addr=06.0,bus=...
    -device scsi-hd,scsi-hd=5,bus=...
    -device ide-hd,unit=1,bus=...
    -device virtserialport,nr=7,bus=...

Note that each of these buses has its own way to specify a slot address,
namely a bus-specific option.

Can you give examples for "single slot that can support multiple
devices"?

> +#
> +# @bus: ID of the bus object where the device can be plugged. Optional,
> +#       as some devices don't need a bus to be plugged (e.g. CPUs).
> +#       Can be copied to the "bus" argument to @device_add.

Suggest something like "Can be used as value for @device_add's bus
option".

Should we give similar information on the slot address?  The option name
is obvious.  What about acceptable values?

> +#
> +# @type: type of device slot.
> +#
> +# @accepted-device-types: List of device types accepted by the slot.
> +#                         Any device plugged to the slot should implement
> +#                         one of the accepted device types.
> +#
> +# @max-devices: #optional maximum number of devices that can be plugged
> +#               to the slot.

What does it mean when @max-devices isn't given?

> +#
> +# @devices: List of QOM paths for devices that are already plugged.
> +#
> +# @available: If false, the slot is not available for plugging any device.
> +#             This value can change at runtime if condition changes
> +#             (e.g. if the device becomes full, or if the machine
> +#             was already initialized and the slot doesn't support
> +#             hotplug).
> +#
> +# @hotpluggable: If true, the slot accepts hotplugged devices.
> +#
> +# DeviceSlotInfo structs always have a @props member, whose members
> +# can be directly copied to the arguments to @device_add.

Do you mean names of properties common to all @accepted-device-types?

> +##
> +{ 'union': 'DeviceSlotInfo',
> +  'base': { 'type': 'DeviceSlotType',
> +            'accepted-device-types': [ 'str' ],
> +            '*max-devices': 'int', 'devices': [ 'str' ],
> +            'available': 'bool', 'hotpluggable': 'bool' },
> +  'discriminator': 'type',
> +  'data': { 'generic': 'GenericSlotInfo',
> +            'pci': 'PCISlotInfo',
> +            'cpu': 'CPUSlotInfo' } }
> +
> +##
> +# @GenericSlotProperties:
> +##
> +{ 'struct': 'GenericSlotProperties',
> +  'data': { 'bus': 'str' } }
> +
> +
> +##
> +# @GenericSlotInfo:
> +#
> +# Generic slot information, with no bus-specific information
> +##
> +{ 'struct': 'GenericSlotInfo',
> +  'data': { 'props': 'GenericSlotProperties' } }
> +
> +##
> +# @PCIDeviceSlotProperties:
> +#
> +# Properties that can be set when plugging a PCI device.
> +#
> +# @addr: "addr" argument to @device_add.
> +#
> +#FIXME: replace @addr with slot and function properties.
> +##
> +{ 'struct': 'PCIDeviceSlotProperties',
> +  'data': { 'bus': 'str', 'addr': 'int' } }
> +
> +##
> +# @PCISlotInfo:
> +#
> +# Information on a PCI device slot.
> +#
> +# @props: The @device_add arguments that can be used when plugging
> +# the device.
> +##
> +{ 'struct': 'PCISlotInfo',
> +  'data': { 'props': 'PCIDeviceSlotProperties' } }
> +
> +##
> +# @CPUSlotInfo:
> +#
> +# Information on a CPU device slot.
> +#
> +# @props: The @device_add arguments that can be used when plugging
> +# the device.
> +##
> +{ 'struct': 'CPUSlotInfo',
> +  'data': { 'props': 'CpuInstanceProperties' } }
> +
> +##
> +# @query-device-slots:
> +#
> +# Return the list of possible slots for plugging devices using
> +# @device_add.
> +##
> +{ 'command': 'query-device-slots',
> +  'returns': [ 'DeviceSlotInfo' ] }
> +
> +##
>  # @MachineBusInfo
>  #
>  # Information about a bus present on a machine.
[...]
Eduardo Habkost Dec. 13, 2016, 12:42 p.m. UTC | #2
On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
> Quick interface review only:
> 
> Eduardo Habkost <ehabkost@redhat.com> writes:
> 
> > This adds a new command to QMP: query-device-slots. It will allow
> > management software to query possible slots where devices can be
> > plugged.
> >
> > This implementation of the command will return:
> >
> > * Multiple PCI slots per bus, in the case of PCI buses;
> > * One slot per bus in the case of the other buses;
> 
> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
> slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
> or two (PATA).  For more examples, see qdev-device-use.txt section
> "Specifying Bus and Address on Bus".

Yes, I should have clarified that: this version changes only PCI
to expose multiple slots, but other buses also need be changed to
implement BusClass::enumerate_slots() properly, too.

> 
> > * One slot for each entry from query-hotpluggable-cpus.
> > In the example below, I am not sure if the PCIe ports are all
> > supposed to report all slots, but I didn't find any existing
> > field in PCIBus that would help me figure out the actual number
> > of slots in a given PCI bus.
> >
> > Git tree
> > --------
> >
> > This patch needs the previous query-machines series I am working
> > on. The full tree can be found on the git tree at:
> >
> >   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
> >
> > Example output
> > --------------
> >
> > The following output was returned by QEMU when running it as:
> >
> >  $ qemu-system-x86_64 -machine q35 \
> >    -readconfig docs/q35-chipset.cfg \
> >    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
> >
> >  {
> >     "return": [
> 
> Are you sure >3000 lines of example output make sense here?

I'm not. :)

That's why I need feedback from the PCI experts. I believe most
of the PCI ports on q35 accept only one device, but I see no code
implementing that restriction, and no obvious PCIBus or
PCIBusClass field indicating that.

> 
> [...]
> >     ]
> >   }
> >
> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> > ---
> >  qapi-schema.json       | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  include/hw/qdev-core.h |   6 +++
> >  hw/core/bus.c          |  49 +++++++++++++++++++++
> >  hw/pci/pci.c           | 106 +++++++++++++++++++++++++++++++++------------
> >  qdev-monitor.c         |  86 ++++++++++++++++++++++++++++++++++---
> >  5 files changed, 328 insertions(+), 33 deletions(-)
> >
> > diff --git a/qapi-schema.json b/qapi-schema.json
> > index d48ff3f..484d91e 100644
> > --- a/qapi-schema.json
> > +++ b/qapi-schema.json
> > @@ -3166,6 +3166,120 @@
> >  { 'command': 'closefd', 'data': {'fdname': 'str'} }
> >  
> >  ##
> > +# @DeviceSlotType:
> > +#
> > +# Type of device slot
> > +#
> > +# @generic: Generic device slot, with no bus-specific information
> > +# @pci: PCI device slot
> > +# @cpu: CPU device slot
> > +##
> > +{ 'enum': 'DeviceSlotType',
> > +  'data': ['generic', 'pci', 'cpu'] }
> > +
> > +##
> > +# @DeviceSlotInfo:
> > +#
> > +# Information on a slot where devices can be plugged. Some buses
> > +# are represented as a single slot that can support multiple devices,
> > +# and some buses have multiple slots that are identified by arguments
> > +# to @device_add.
> 
> Okay, let me try to wrap my poor, ignorant mind around this.
> 
> There are two kinds of buses: "single slot that can support multiple
> devices", and "multiple slots that are identified by arguments".
> 
> How are the two kinds related to @type?

They are related to @type indirectly because different bus types
will return different data. But I don't want to make this part of
the specification: clients should be prepared to handle both
cases.

e.g. a QEMU version might return a single generic catch-all
sysbus-device slot that support any number of devices. Future
versions may return more detailed information, and return slots
only for the sysbus devices that really work with the machine.

> 
> Examples for "multiple slots that are identified by arguments":
> 
>     -device edu,addr=06.0,bus=...
>     -device scsi-hd,scsi-hd=5,bus=...
>     -device ide-hd,unit=1,bus=...
>     -device virtserialport,nr=7,bus=...
> 
> Note that each of these buses has its own way to specify a slot address,
> namely a bus-specific option.

Yes.

> 
> Can you give examples for "single slot that can support multiple
> devices"?

I can't name any example except sysbus, right now.

There are two cases that I tried to cover by reporting
multiple-device no-argument slots:

1) Buses that were not converted (yet) to implement
   enumerate_buses() (in the current version: everything except
   PCI)
2) Buses that really do not require any extra slot address
   argument (sysbus? others?)

I'm proposing this interface because I don't want (1) or (2) to
be missing from the returned data (otherwise management software
can't differentiate "this bus is not available" from "this bus
simply doesn't implement slot enumeration yet").

We won't need the special multi-device-slot case if we eliminate
all instances of (1) and (2). Can we do that in 2.9? I'm not
sure.

> 
> > +#
> > +# @bus: ID of the bus object where the device can be plugged. Optional,
> > +#       as some devices don't need a bus to be plugged (e.g. CPUs).
> > +#       Can be copied to the "bus" argument to @device_add.
> 
> Suggest something like "Can be used as value for @device_add's bus
> option".

Will do.

> 
> Should we give similar information on the slot address?  The option name
> is obvious.  What about acceptable values?

Actually, this part of the documentation was a leftover from a
previous version where @bus was inside DeviceSlotInfo. Now I
moved @bus inside @props for all device types, and it should be
documented the same way as the other fields inside @props.

> 
> > +#
> > +# @type: type of device slot.
> > +#
> > +# @accepted-device-types: List of device types accepted by the slot.
> > +#                         Any device plugged to the slot should implement
> > +#                         one of the accepted device types.
> > +#
> > +# @max-devices: #optional maximum number of devices that can be plugged
> > +#               to the slot.
> 
> What does it mean when @max-devices isn't given?

It means there is no hard limit on the number of pluggable
devices. sysbus is one of such cases.

> 
> > +#
> > +# @devices: List of QOM paths for devices that are already plugged.
> > +#
> > +# @available: If false, the slot is not available for plugging any device.
> > +#             This value can change at runtime if condition changes
> > +#             (e.g. if the device becomes full, or if the machine
> > +#             was already initialized and the slot doesn't support
> > +#             hotplug).
> > +#
> > +# @hotpluggable: If true, the slot accepts hotplugged devices.
> > +#
> > +# DeviceSlotInfo structs always have a @props member, whose members
> > +# can be directly copied to the arguments to @device_add.
> 
> Do you mean names of properties common to all @accepted-device-types?

I mean that it should be safe to simply use the keys+values in
@props as arguments to device_add or -device, for any slot @type.
Some clients may want to extract some information from @props (if
they already manage PCI addresses, for example), but some of them
may simply choose any available slot and copy @props blindly.

I didn't want to state anything about QOM properties, because I
only want @props to represent device_add/-device arguments. Some
of those arguments are consumed by the device_add code itself
(e.g. "bus") and others are translated to QOM properties. I don't
want the interface to impose any restrictions on how this is
implemented.

> 
> > +##
> > +{ 'union': 'DeviceSlotInfo',
> > +  'base': { 'type': 'DeviceSlotType',
> > +            'accepted-device-types': [ 'str' ],
> > +            '*max-devices': 'int', 'devices': [ 'str' ],
> > +            'available': 'bool', 'hotpluggable': 'bool' },
> > +  'discriminator': 'type',
> > +  'data': { 'generic': 'GenericSlotInfo',
> > +            'pci': 'PCISlotInfo',
> > +            'cpu': 'CPUSlotInfo' } }
> > +
> > +##
> > +# @GenericSlotProperties:
> > +##
> > +{ 'struct': 'GenericSlotProperties',
> > +  'data': { 'bus': 'str' } }
> > +
> > +
> > +##
> > +# @GenericSlotInfo:
> > +#
> > +# Generic slot information, with no bus-specific information
> > +##
> > +{ 'struct': 'GenericSlotInfo',
> > +  'data': { 'props': 'GenericSlotProperties' } }
> > +
> > +##
> > +# @PCIDeviceSlotProperties:
> > +#
> > +# Properties that can be set when plugging a PCI device.
> > +#
> > +# @addr: "addr" argument to @device_add.
> > +#
> > +#FIXME: replace @addr with slot and function properties.
> > +##
> > +{ 'struct': 'PCIDeviceSlotProperties',
> > +  'data': { 'bus': 'str', 'addr': 'int' } }
> > +
> > +##
> > +# @PCISlotInfo:
> > +#
> > +# Information on a PCI device slot.
> > +#
> > +# @props: The @device_add arguments that can be used when plugging
> > +# the device.
> > +##
> > +{ 'struct': 'PCISlotInfo',
> > +  'data': { 'props': 'PCIDeviceSlotProperties' } }
> > +
> > +##
> > +# @CPUSlotInfo:
> > +#
> > +# Information on a CPU device slot.
> > +#
> > +# @props: The @device_add arguments that can be used when plugging
> > +# the device.
> > +##
> > +{ 'struct': 'CPUSlotInfo',
> > +  'data': { 'props': 'CpuInstanceProperties' } }
> > +
> > +##
> > +# @query-device-slots:
> > +#
> > +# Return the list of possible slots for plugging devices using
> > +# @device_add.
> > +##
> > +{ 'command': 'query-device-slots',
> > +  'returns': [ 'DeviceSlotInfo' ] }
> > +
> > +##
> >  # @MachineBusInfo
> >  #
> >  # Information about a bus present on a machine.
> [...]
Marcel Apfelbaum Dec. 13, 2016, 2:15 p.m. UTC | #3
On 12/13/2016 02:42 PM, Eduardo Habkost wrote:
> On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
>> Quick interface review only:
>>
>> Eduardo Habkost <ehabkost@redhat.com> writes:
>>
>>> This adds a new command to QMP: query-device-slots. It will allow
>>> management software to query possible slots where devices can be
>>> plugged.
>>>
>>> This implementation of the command will return:
>>>
>>> * Multiple PCI slots per bus, in the case of PCI buses;
>>> * One slot per bus in the case of the other buses;
>>
>> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
>> slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
>> or two (PATA).  For more examples, see qdev-device-use.txt section
>> "Specifying Bus and Address on Bus".
>
> Yes, I should have clarified that: this version changes only PCI
> to expose multiple slots, but other buses also need be changed to
> implement BusClass::enumerate_slots() properly, too.
>
>>
>>> * One slot for each entry from query-hotpluggable-cpus.
>>> In the example below, I am not sure if the PCIe ports are all
>>> supposed to report all slots, but I didn't find any existing
>>> field in PCIBus that would help me figure out the actual number
>>> of slots in a given PCI bus.
>>>
>>> Git tree
>>> --------
>>>
>>> This patch needs the previous query-machines series I am working
>>> on. The full tree can be found on the git tree at:
>>>
>>>   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
>>>
>>> Example output
>>> --------------
>>>
>>> The following output was returned by QEMU when running it as:
>>>
>>>  $ qemu-system-x86_64 -machine q35 \
>>>    -readconfig docs/q35-chipset.cfg \
>>>    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
>>>
>>>  {
>>>     "return": [
>>
>> Are you sure >3000 lines of example output make sense here?
>
> I'm not. :)
>
> That's why I need feedback from the PCI experts. I believe most
> of the PCI ports on q35 accept only one device, but I see no code
> implementing that restriction, and no obvious PCIBus or
> PCIBusClass field indicating that.

Hi,

Sadly there is no such restriction today as far as I know.
We trust the user that he knows what he is doing :)
and we let libvirt implement the restriction.

I do agree we should fix that.

Thanks,
Marcel

>
>>
>> [...]

[...]
Eduardo Habkost Dec. 13, 2016, 4 p.m. UTC | #4
On Tue, Dec 13, 2016 at 04:15:18PM +0200, Marcel Apfelbaum wrote:
> On 12/13/2016 02:42 PM, Eduardo Habkost wrote:
> > On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
> > > Quick interface review only:
> > > 
> > > Eduardo Habkost <ehabkost@redhat.com> writes:
> > > 
> > > > This adds a new command to QMP: query-device-slots. It will allow
> > > > management software to query possible slots where devices can be
> > > > plugged.
> > > > 
> > > > This implementation of the command will return:
> > > > 
> > > > * Multiple PCI slots per bus, in the case of PCI buses;
> > > > * One slot per bus in the case of the other buses;
> > > 
> > > Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
> > > slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
> > > or two (PATA).  For more examples, see qdev-device-use.txt section
> > > "Specifying Bus and Address on Bus".
> > 
> > Yes, I should have clarified that: this version changes only PCI
> > to expose multiple slots, but other buses also need be changed to
> > implement BusClass::enumerate_slots() properly, too.
> > 
> > > 
> > > > * One slot for each entry from query-hotpluggable-cpus.
> > > > In the example below, I am not sure if the PCIe ports are all
> > > > supposed to report all slots, but I didn't find any existing
> > > > field in PCIBus that would help me figure out the actual number
> > > > of slots in a given PCI bus.
> > > > 
> > > > Git tree
> > > > --------
> > > > 
> > > > This patch needs the previous query-machines series I am working
> > > > on. The full tree can be found on the git tree at:
> > > > 
> > > >   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
> > > > 
> > > > Example output
> > > > --------------
> > > > 
> > > > The following output was returned by QEMU when running it as:
> > > > 
> > > >  $ qemu-system-x86_64 -machine q35 \
> > > >    -readconfig docs/q35-chipset.cfg \
> > > >    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
> > > > 
> > > >  {
> > > >     "return": [
> > > 
> > > Are you sure >3000 lines of example output make sense here?
> > 
> > I'm not. :)
> > 
> > That's why I need feedback from the PCI experts. I believe most
> > of the PCI ports on q35 accept only one device, but I see no code
> > implementing that restriction, and no obvious PCIBus or
> > PCIBusClass field indicating that.
> 
> Hi,
> 
> Sadly there is no such restriction today as far as I know.
> We trust the user that he knows what he is doing :)
> and we let libvirt implement the restriction.
> 
> I do agree we should fix that.

OK, this matches what I saw on the current code.

But I would like to understand what exactly should be the rules
if we fix that. Should we report only 1 slot if
pci_bus_has_pcie_upstream_port(bus)?

(Note that pci_bus_has_pcie_upstream_port() doesn't exist in the
current code, it is added by my patch)
Markus Armbruster Dec. 13, 2016, 7:51 p.m. UTC | #5
Eduardo Habkost <ehabkost@redhat.com> writes:

> On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
>> Quick interface review only:
>> 
>> Eduardo Habkost <ehabkost@redhat.com> writes:
>> 
>> > This adds a new command to QMP: query-device-slots. It will allow
>> > management software to query possible slots where devices can be
>> > plugged.
>> >
>> > This implementation of the command will return:
>> >
>> > * Multiple PCI slots per bus, in the case of PCI buses;
>> > * One slot per bus in the case of the other buses;
>> 
>> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
>> slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
>> or two (PATA).  For more examples, see qdev-device-use.txt section
>> "Specifying Bus and Address on Bus".
>
> Yes, I should have clarified that: this version changes only PCI
> to expose multiple slots, but other buses also need be changed to
> implement BusClass::enumerate_slots() properly, too.
>
>> 
>> > * One slot for each entry from query-hotpluggable-cpus.
>> > In the example below, I am not sure if the PCIe ports are all
>> > supposed to report all slots, but I didn't find any existing
>> > field in PCIBus that would help me figure out the actual number
>> > of slots in a given PCI bus.
>> >
>> > Git tree
>> > --------
>> >
>> > This patch needs the previous query-machines series I am working
>> > on. The full tree can be found on the git tree at:
>> >
>> >   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
>> >
>> > Example output
>> > --------------
>> >
>> > The following output was returned by QEMU when running it as:
>> >
>> >  $ qemu-system-x86_64 -machine q35 \
>> >    -readconfig docs/q35-chipset.cfg \
>> >    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
>> >
>> >  {
>> >     "return": [
>> 
>> Are you sure >3000 lines of example output make sense here?
>
> I'm not. :)
>
> That's why I need feedback from the PCI experts. I believe most
> of the PCI ports on q35 accept only one device, but I see no code
> implementing that restriction, and no obvious PCIBus or
> PCIBusClass field indicating that.
>
>> 
>> [...]
>> >     ]
>> >   }
>> >
>> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
>> > ---
>> >  qapi-schema.json       | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
>> >  include/hw/qdev-core.h |   6 +++
>> >  hw/core/bus.c          |  49 +++++++++++++++++++++
>> >  hw/pci/pci.c           | 106 +++++++++++++++++++++++++++++++++------------
>> >  qdev-monitor.c         |  86 ++++++++++++++++++++++++++++++++++---
>> >  5 files changed, 328 insertions(+), 33 deletions(-)
>> >
>> > diff --git a/qapi-schema.json b/qapi-schema.json
>> > index d48ff3f..484d91e 100644
>> > --- a/qapi-schema.json
>> > +++ b/qapi-schema.json
>> > @@ -3166,6 +3166,120 @@
>> >  { 'command': 'closefd', 'data': {'fdname': 'str'} }
>> >  
>> >  ##
>> > +# @DeviceSlotType:
>> > +#
>> > +# Type of device slot
>> > +#
>> > +# @generic: Generic device slot, with no bus-specific information
>> > +# @pci: PCI device slot
>> > +# @cpu: CPU device slot
>> > +##
>> > +{ 'enum': 'DeviceSlotType',
>> > +  'data': ['generic', 'pci', 'cpu'] }
>> > +
>> > +##
>> > +# @DeviceSlotInfo:
>> > +#
>> > +# Information on a slot where devices can be plugged. Some buses
>> > +# are represented as a single slot that can support multiple devices,
>> > +# and some buses have multiple slots that are identified by arguments
>> > +# to @device_add.
>> 
>> Okay, let me try to wrap my poor, ignorant mind around this.
>> 
>> There are two kinds of buses: "single slot that can support multiple
>> devices", and "multiple slots that are identified by arguments".
>> 
>> How are the two kinds related to @type?
>
> They are related to @type indirectly because different bus types
> will return different data. But I don't want to make this part of
> the specification: clients should be prepared to handle both
> cases.

Well, color me confused :)

> e.g. a QEMU version might return a single generic catch-all
> sysbus-device slot that support any number of devices. Future
> versions may return more detailed information, and return slots
> only for the sysbus devices that really work with the machine.

Hmm.  See below.

>> 
>> Examples for "multiple slots that are identified by arguments":
>> 
>>     -device edu,addr=06.0,bus=...
>>     -device scsi-hd,scsi-hd=5,bus=...
>>     -device ide-hd,unit=1,bus=...
>>     -device virtserialport,nr=7,bus=...
>> 
>> Note that each of these buses has its own way to specify a slot address,
>> namely a bus-specific option.
>
> Yes.
>
>> 
>> Can you give examples for "single slot that can support multiple
>> devices"?
>
> I can't name any example except sysbus, right now.

Sysbus is a bad example, because it's a hack, not a bus.

Physical devices are wired up in some device-specific way.  An important
special wiring case is plugging into a standard socket provided by a
bus.  But not every device is connected only to a bus.  Devices can also
be wired to other devices in ad hoc ways.

In the initial qdev design, devices could only plug into buses.
Anything that didn't fit the mold was declared to plug into the "system
bus", which isn't a bus at all.

The hack used to be fairly harmless, because you couldn't do much with
system bus devices anyway.  Not true anymore: Alex created means to add
certain system bus devices to certain machines with -device.  Has been a
thorn in my side ever since.  I'm afraid we need to understand how
exactly it works before we can finalize the design for this command.

> There are two cases that I tried to cover by reporting
> multiple-device no-argument slots:
>
> 1) Buses that were not converted (yet) to implement
>    enumerate_buses() (in the current version: everything except
>    PCI)
> 2) Buses that really do not require any extra slot address
>    argument (sysbus? others?)
>
> I'm proposing this interface because I don't want (1) or (2) to
> be missing from the returned data (otherwise management software
> can't differentiate "this bus is not available" from "this bus
> simply doesn't implement slot enumeration yet").
>
> We won't need the special multi-device-slot case if we eliminate
> all instances of (1) and (2). Can we do that in 2.9? I'm not
> sure.

If we can't do a complete job, and we don't want to hide slots affected
by that, the data we return for them should unequivocally state that
it's incomplete because computing complete data hasn't been implemented,
yet.

>> > +#
>> > +# @bus: ID of the bus object where the device can be plugged. Optional,
>> > +#       as some devices don't need a bus to be plugged (e.g. CPUs).
>> > +#       Can be copied to the "bus" argument to @device_add.
>> 
>> Suggest something like "Can be used as value for @device_add's bus
>> option".
>
> Will do.
>
>> 
>> Should we give similar information on the slot address?  The option name
>> is obvious.  What about acceptable values?
>
> Actually, this part of the documentation was a leftover from a
> previous version where @bus was inside DeviceSlotInfo. Now I
> moved @bus inside @props for all device types, and it should be
> documented the same way as the other fields inside @props.
>
>> 
>> > +#
>> > +# @type: type of device slot.
>> > +#
>> > +# @accepted-device-types: List of device types accepted by the slot.
>> > +#                         Any device plugged to the slot should implement
>> > +#                         one of the accepted device types.
>> > +#
>> > +# @max-devices: #optional maximum number of devices that can be plugged
>> > +#               to the slot.
>> 
>> What does it mean when @max-devices isn't given?
>
> It means there is no hard limit on the number of pluggable
> devices. sysbus is one of such cases.

Sysbus certainly has limits, but they're more complicated than just "at
most @max-devices devices".

>> > +#
>> > +# @devices: List of QOM paths for devices that are already plugged.
>> > +#
>> > +# @available: If false, the slot is not available for plugging any device.
>> > +#             This value can change at runtime if condition changes
>> > +#             (e.g. if the device becomes full, or if the machine
>> > +#             was already initialized and the slot doesn't support
>> > +#             hotplug).
>> > +#
>> > +# @hotpluggable: If true, the slot accepts hotplugged devices.
>> > +#
>> > +# DeviceSlotInfo structs always have a @props member, whose members
>> > +# can be directly copied to the arguments to @device_add.
>> 
>> Do you mean names of properties common to all @accepted-device-types?
>
> I mean that it should be safe to simply use the keys+values in
> @props as arguments to device_add or -device, for any slot @type.
> Some clients may want to extract some information from @props (if
> they already manage PCI addresses, for example), but some of them
> may simply choose any available slot and copy @props blindly.
>
> I didn't want to state anything about QOM properties, because I
> only want @props to represent device_add/-device arguments. Some
> of those arguments are consumed by the device_add code itself
> (e.g. "bus") and others are translated to QOM properties. I don't
> want the interface to impose any restrictions on how this is
> implemented.

Hmm, is this meant to work as follows?  To plug a device into this slot,
you use

    { "execute": "device_add",
      "arguments": { "driver": TYPE, ... PROPS ... }

where TYPE is a (concrete subtype of a) member of
@accepted-device-types, and PROPS is the value of @props spliced in.
Example: the data for slot 06.0 of PCI bus pci.0 would be something like

    {
      "bus": "pci.0",
      "props": {
          "bus": "pci.0",
          "addr": "06.0"
      }
    }

Doesn't match your example output; I guess I'm still misunderstanding
something.

>
>> 
>> > +##
>> > +{ 'union': 'DeviceSlotInfo',
>> > +  'base': { 'type': 'DeviceSlotType',
>> > +            'accepted-device-types': [ 'str' ],
>> > +            '*max-devices': 'int', 'devices': [ 'str' ],
>> > +            'available': 'bool', 'hotpluggable': 'bool' },
>> > +  'discriminator': 'type',
>> > +  'data': { 'generic': 'GenericSlotInfo',
>> > +            'pci': 'PCISlotInfo',
>> > +            'cpu': 'CPUSlotInfo' } }
>> > +
>> > +##
>> > +# @GenericSlotProperties:
>> > +##
>> > +{ 'struct': 'GenericSlotProperties',
>> > +  'data': { 'bus': 'str' } }
>> > +
>> > +
>> > +##
>> > +# @GenericSlotInfo:
>> > +#
>> > +# Generic slot information, with no bus-specific information
>> > +##
>> > +{ 'struct': 'GenericSlotInfo',
>> > +  'data': { 'props': 'GenericSlotProperties' } }
>> > +
>> > +##
>> > +# @PCIDeviceSlotProperties:
>> > +#
>> > +# Properties that can be set when plugging a PCI device.
>> > +#
>> > +# @addr: "addr" argument to @device_add.
>> > +#
>> > +#FIXME: replace @addr with slot and function properties.
>> > +##
>> > +{ 'struct': 'PCIDeviceSlotProperties',
>> > +  'data': { 'bus': 'str', 'addr': 'int' } }
>> > +
>> > +##
>> > +# @PCISlotInfo:
>> > +#
>> > +# Information on a PCI device slot.
>> > +#
>> > +# @props: The @device_add arguments that can be used when plugging
>> > +# the device.
>> > +##
>> > +{ 'struct': 'PCISlotInfo',
>> > +  'data': { 'props': 'PCIDeviceSlotProperties' } }
>> > +
>> > +##
>> > +# @CPUSlotInfo:
>> > +#
>> > +# Information on a CPU device slot.
>> > +#
>> > +# @props: The @device_add arguments that can be used when plugging
>> > +# the device.
>> > +##
>> > +{ 'struct': 'CPUSlotInfo',
>> > +  'data': { 'props': 'CpuInstanceProperties' } }
>> > +
>> > +##
>> > +# @query-device-slots:
>> > +#
>> > +# Return the list of possible slots for plugging devices using
>> > +# @device_add.
>> > +##
>> > +{ 'command': 'query-device-slots',
>> > +  'returns': [ 'DeviceSlotInfo' ] }
>> > +
>> > +##
>> >  # @MachineBusInfo
>> >  #
>> >  # Information about a bus present on a machine.
>> [...]
Eduardo Habkost Dec. 13, 2016, 8:59 p.m. UTC | #6
On Tue, Dec 13, 2016 at 08:51:34PM +0100, Markus Armbruster wrote:
> Eduardo Habkost <ehabkost@redhat.com> writes:
> 
> > On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
> >> Quick interface review only:
> >> 
> >> Eduardo Habkost <ehabkost@redhat.com> writes:
> >> 
> >> > This adds a new command to QMP: query-device-slots. It will allow
> >> > management software to query possible slots where devices can be
> >> > plugged.
> >> >
> >> > This implementation of the command will return:
> >> >
> >> > * Multiple PCI slots per bus, in the case of PCI buses;
> >> > * One slot per bus in the case of the other buses;
> >> 
> >> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
> >> slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
> >> or two (PATA).  For more examples, see qdev-device-use.txt section
> >> "Specifying Bus and Address on Bus".
> >
> > Yes, I should have clarified that: this version changes only PCI
> > to expose multiple slots, but other buses also need be changed to
> > implement BusClass::enumerate_slots() properly, too.
> >
> >> 
> >> > * One slot for each entry from query-hotpluggable-cpus.
> >> > In the example below, I am not sure if the PCIe ports are all
> >> > supposed to report all slots, but I didn't find any existing
> >> > field in PCIBus that would help me figure out the actual number
> >> > of slots in a given PCI bus.
> >> >
> >> > Git tree
> >> > --------
> >> >
> >> > This patch needs the previous query-machines series I am working
> >> > on. The full tree can be found on the git tree at:
> >> >
> >> >   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
> >> >
> >> > Example output
> >> > --------------
> >> >
> >> > The following output was returned by QEMU when running it as:
> >> >
> >> >  $ qemu-system-x86_64 -machine q35 \
> >> >    -readconfig docs/q35-chipset.cfg \
> >> >    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
> >> >
> >> >  {
> >> >     "return": [
> >> 
> >> Are you sure >3000 lines of example output make sense here?
> >
> > I'm not. :)
> >
> > That's why I need feedback from the PCI experts. I believe most
> > of the PCI ports on q35 accept only one device, but I see no code
> > implementing that restriction, and no obvious PCIBus or
> > PCIBusClass field indicating that.
> >
> >> 
> >> [...]
> >> >     ]
> >> >   }
> >> >
> >> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> >> > ---
> >> >  qapi-schema.json       | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
> >> >  include/hw/qdev-core.h |   6 +++
> >> >  hw/core/bus.c          |  49 +++++++++++++++++++++
> >> >  hw/pci/pci.c           | 106 +++++++++++++++++++++++++++++++++------------
> >> >  qdev-monitor.c         |  86 ++++++++++++++++++++++++++++++++++---
> >> >  5 files changed, 328 insertions(+), 33 deletions(-)
> >> >
> >> > diff --git a/qapi-schema.json b/qapi-schema.json
> >> > index d48ff3f..484d91e 100644
> >> > --- a/qapi-schema.json
> >> > +++ b/qapi-schema.json
> >> > @@ -3166,6 +3166,120 @@
> >> >  { 'command': 'closefd', 'data': {'fdname': 'str'} }
> >> >  
> >> >  ##
> >> > +# @DeviceSlotType:
> >> > +#
> >> > +# Type of device slot
> >> > +#
> >> > +# @generic: Generic device slot, with no bus-specific information
> >> > +# @pci: PCI device slot
> >> > +# @cpu: CPU device slot
> >> > +##
> >> > +{ 'enum': 'DeviceSlotType',
> >> > +  'data': ['generic', 'pci', 'cpu'] }
> >> > +
> >> > +##
> >> > +# @DeviceSlotInfo:
> >> > +#
> >> > +# Information on a slot where devices can be plugged. Some buses
> >> > +# are represented as a single slot that can support multiple devices,
> >> > +# and some buses have multiple slots that are identified by arguments
> >> > +# to @device_add.
> >> 
> >> Okay, let me try to wrap my poor, ignorant mind around this.
> >> 
> >> There are two kinds of buses: "single slot that can support multiple
> >> devices", and "multiple slots that are identified by arguments".
> >> 
> >> How are the two kinds related to @type?
> >
> > They are related to @type indirectly because different bus types
> > will return different data. But I don't want to make this part of
> > the specification: clients should be prepared to handle both
> > cases.
> 
> Well, color me confused :)
> 
> > e.g. a QEMU version might return a single generic catch-all
> > sysbus-device slot that support any number of devices. Future
> > versions may return more detailed information, and return slots
> > only for the sysbus devices that really work with the machine.
> 
> Hmm.  See below.
> 
> >> 
> >> Examples for "multiple slots that are identified by arguments":
> >> 
> >>     -device edu,addr=06.0,bus=...
> >>     -device scsi-hd,scsi-hd=5,bus=...
> >>     -device ide-hd,unit=1,bus=...
> >>     -device virtserialport,nr=7,bus=...
> >> 
> >> Note that each of these buses has its own way to specify a slot address,
> >> namely a bus-specific option.
> >
> > Yes.
> >
> >> 
> >> Can you give examples for "single slot that can support multiple
> >> devices"?
> >
> > I can't name any example except sysbus, right now.
> 
> Sysbus is a bad example, because it's a hack, not a bus.
> 
> Physical devices are wired up in some device-specific way.  An important
> special wiring case is plugging into a standard socket provided by a
> bus.  But not every device is connected only to a bus.  Devices can also
> be wired to other devices in ad hoc ways.
> 
> In the initial qdev design, devices could only plug into buses.
> Anything that didn't fit the mold was declared to plug into the "system
> bus", which isn't a bus at all.
> 
> The hack used to be fairly harmless, because you couldn't do much with
> system bus devices anyway.  Not true anymore: Alex created means to add
> certain system bus devices to certain machines with -device.  Has been a
> thorn in my side ever since.  I'm afraid we need to understand how
> exactly it works before we can finalize the design for this command.

I agree it is a hack, I just wanted to be able to cover it too
(and possibly other cases where there's no obvious "address"
parameter to devices).

The other options I had were:
a) Omitting the bus from the output;
b) Waiting until we fix sysbus before implementing the interface.

I avoided (a) because I would like to tell libvirt "always check
query-device-slots before plugging anything. if it's not there,
it means you can't plug it". To do that, I would need to ensure
all buses are present in the list (even sysbus).

(But as I say below: I don't love this solution and I am open to
suggestions).

> 
> > There are two cases that I tried to cover by reporting
> > multiple-device no-argument slots:
> >
> > 1) Buses that were not converted (yet) to implement
> >    enumerate_buses() (in the current version: everything except
> >    PCI)
> > 2) Buses that really do not require any extra slot address
> >    argument (sysbus? others?)
> >
> > I'm proposing this interface because I don't want (1) or (2) to
> > be missing from the returned data (otherwise management software
> > can't differentiate "this bus is not available" from "this bus
> > simply doesn't implement slot enumeration yet").
> >
> > We won't need the special multi-device-slot case if we eliminate
> > all instances of (1) and (2). Can we do that in 2.9? I'm not
> > sure.
> 
> If we can't do a complete job, and we don't want to hide slots affected
> by that, the data we return for them should unequivocally state that
> it's incomplete because computing complete data hasn't been implemented,
> yet.

OK, so we agree we need a way to tell the client the data is
incomplete (or not as detailed as we would like).

I tried to solve that by providing a mechanism to tell the client
"look, I can tell you that there's a bus called <bus name>, and
it is valid to use it as the 'bus' argument for -device and
device_add, but I don't know the rest of the rules (sorry!)". We
can do that by simply including the bus on the list as if it was
a multi-device slot.

But I don't love this solution, so I am open to other suggestions
on how to tell the client that slot information is incomplete for
a bus.

Maybe the following?

1) Add a new DeviceSlotType value meaning "this entry represents
   a bus/device-type that can't enumerate its slots" (let's call
   it "bus-with-no-slot-info" by now).
2) Remove DeviceSlotType "generic".
3) Remove max-devices field, and simply document the device-limit
   semantics for each DeviceSlotType (cpu: 1 device, pci: 1
   device, bus-with-no-slot-info: undefined limit).

This looks like a trade-off between moving data to specific union
types (and representing type-specific limits/rules in the schema
documentation), or moving data to the base union type (and
representing type-specific limits as data in the base union
type).

> 
> >> > +#
> >> > +# @bus: ID of the bus object where the device can be plugged. Optional,
> >> > +#       as some devices don't need a bus to be plugged (e.g. CPUs).
> >> > +#       Can be copied to the "bus" argument to @device_add.
> >> 
> >> Suggest something like "Can be used as value for @device_add's bus
> >> option".
> >
> > Will do.
> >
> >> 
> >> Should we give similar information on the slot address?  The option name
> >> is obvious.  What about acceptable values?
> >
> > Actually, this part of the documentation was a leftover from a
> > previous version where @bus was inside DeviceSlotInfo. Now I
> > moved @bus inside @props for all device types, and it should be
> > documented the same way as the other fields inside @props.
> >
> >> 
> >> > +#
> >> > +# @type: type of device slot.
> >> > +#
> >> > +# @accepted-device-types: List of device types accepted by the slot.
> >> > +#                         Any device plugged to the slot should implement
> >> > +#                         one of the accepted device types.
> >> > +#
> >> > +# @max-devices: #optional maximum number of devices that can be plugged
> >> > +#               to the slot.
> >> 
> >> What does it mean when @max-devices isn't given?
> >
> > It means there is no hard limit on the number of pluggable
> > devices. sysbus is one of such cases.
> 
> Sysbus certainly has limits, but they're more complicated than just "at
> most @max-devices devices".
> 
> >> > +#
> >> > +# @devices: List of QOM paths for devices that are already plugged.
> >> > +#
> >> > +# @available: If false, the slot is not available for plugging any device.
> >> > +#             This value can change at runtime if condition changes
> >> > +#             (e.g. if the device becomes full, or if the machine
> >> > +#             was already initialized and the slot doesn't support
> >> > +#             hotplug).
> >> > +#
> >> > +# @hotpluggable: If true, the slot accepts hotplugged devices.
> >> > +#
> >> > +# DeviceSlotInfo structs always have a @props member, whose members
> >> > +# can be directly copied to the arguments to @device_add.
> >> 
> >> Do you mean names of properties common to all @accepted-device-types?
> >
> > I mean that it should be safe to simply use the keys+values in
> > @props as arguments to device_add or -device, for any slot @type.
> > Some clients may want to extract some information from @props (if
> > they already manage PCI addresses, for example), but some of them
> > may simply choose any available slot and copy @props blindly.
> >
> > I didn't want to state anything about QOM properties, because I
> > only want @props to represent device_add/-device arguments. Some
> > of those arguments are consumed by the device_add code itself
> > (e.g. "bus") and others are translated to QOM properties. I don't
> > want the interface to impose any restrictions on how this is
> > implemented.
> 
> Hmm, is this meant to work as follows?  To plug a device into this slot,
> you use
> 
>     { "execute": "device_add",
>       "arguments": { "driver": TYPE, ... PROPS ... }
> 
> where TYPE is a (concrete subtype of a) member of
> @accepted-device-types, and PROPS is the value of @props spliced in.
> Example: the data for slot 06.0 of PCI bus pci.0 would be something like
> 
>     {
>       "bus": "pci.0",

There's no "bus" field in DeviceSlotInfo. It was a documentation bug.

>       "props": {
>           "bus": "pci.0",
>           "addr": "06.0"
>       }
>     }
> 
> Doesn't match your example output; I guess I'm still misunderstanding
> something.

This is a busy PCI slot, from my example:

        {
            "available": false,
            "devices": [
                "/machine/q35/mch"
            ],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 0
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },

This is an empty PCI slot from my example. It has available=false because
hotplug is not supported on the root bus:

        {
            "available": false,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device",
                "pci-express-device"  
            ],
            "props": {
                "bus": "/machine/q35/pcie.0",
                "addr": 32
            },
            "hotpluggable": false,
            "type": "pci",
            "max-devices": 1
        },

This is an empty PCI slot on pc_piix (not on my example):

        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "legacy-pci-device"
            ],
            "props": {
                "bus": "/machine/i440fx/pci.0",
                "addr": 32
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },

This is a free PCIe slot, from my example:

        {
            "available": true,
            "devices": [],
            "accepted-device-types": [
                "pci-express-device"
            ],
            "props": {
                "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
                "addr": 0
            },
            "hotpluggable": true,
            "type": "pci",
            "max-devices": 1
        },

Note that it returns the uint32 value of "addr" (there's some magic on the
devfn setter to make it accept both "<slot>[.<function>]" strings and <devfn>
integers. On the next version, I plan to fix this insanity and replace "addr"
with proper "slot" and "function" integer properties. (See FIXME below)

> 
> >
> >> 
> >> > +##
> >> > +{ 'union': 'DeviceSlotInfo',
> >> > +  'base': { 'type': 'DeviceSlotType',
> >> > +            'accepted-device-types': [ 'str' ],
> >> > +            '*max-devices': 'int', 'devices': [ 'str' ],
> >> > +            'available': 'bool', 'hotpluggable': 'bool' },
> >> > +  'discriminator': 'type',
> >> > +  'data': { 'generic': 'GenericSlotInfo',
> >> > +            'pci': 'PCISlotInfo',
> >> > +            'cpu': 'CPUSlotInfo' } }
> >> > +
> >> > +##
> >> > +# @GenericSlotProperties:
> >> > +##
> >> > +{ 'struct': 'GenericSlotProperties',
> >> > +  'data': { 'bus': 'str' } }
> >> > +
> >> > +
> >> > +##
> >> > +# @GenericSlotInfo:
> >> > +#
> >> > +# Generic slot information, with no bus-specific information
> >> > +##
> >> > +{ 'struct': 'GenericSlotInfo',
> >> > +  'data': { 'props': 'GenericSlotProperties' } }
> >> > +
> >> > +##
> >> > +# @PCIDeviceSlotProperties:
> >> > +#
> >> > +# Properties that can be set when plugging a PCI device.
> >> > +#
> >> > +# @addr: "addr" argument to @device_add.
> >> > +#
> >> > +#FIXME: replace @addr with slot and function properties.
> >> > +##
> >> > +{ 'struct': 'PCIDeviceSlotProperties',
> >> > +  'data': { 'bus': 'str', 'addr': 'int' } }
> >> > +
> >> > +##
> >> > +# @PCISlotInfo:
> >> > +#
> >> > +# Information on a PCI device slot.
> >> > +#
> >> > +# @props: The @device_add arguments that can be used when plugging
> >> > +# the device.
> >> > +##
> >> > +{ 'struct': 'PCISlotInfo',
> >> > +  'data': { 'props': 'PCIDeviceSlotProperties' } }
> >> > +
> >> > +##
> >> > +# @CPUSlotInfo:
> >> > +#
> >> > +# Information on a CPU device slot.
> >> > +#
> >> > +# @props: The @device_add arguments that can be used when plugging
> >> > +# the device.
> >> > +##
> >> > +{ 'struct': 'CPUSlotInfo',
> >> > +  'data': { 'props': 'CpuInstanceProperties' } }
> >> > +
> >> > +##
> >> > +# @query-device-slots:
> >> > +#
> >> > +# Return the list of possible slots for plugging devices using
> >> > +# @device_add.
> >> > +##
> >> > +{ 'command': 'query-device-slots',
> >> > +  'returns': [ 'DeviceSlotInfo' ] }
> >> > +
> >> > +##
> >> >  # @MachineBusInfo
> >> >  #
> >> >  # Information about a bus present on a machine.
> >> [...]
Markus Armbruster Dec. 14, 2016, 3:34 p.m. UTC | #7
Eduardo Habkost <ehabkost@redhat.com> writes:

> On Tue, Dec 13, 2016 at 08:51:34PM +0100, Markus Armbruster wrote:
>> Eduardo Habkost <ehabkost@redhat.com> writes:
>> 
>> > On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
>> >> Quick interface review only:
>> >> 
>> >> Eduardo Habkost <ehabkost@redhat.com> writes:
>> >> 
>> >> > This adds a new command to QMP: query-device-slots. It will allow
>> >> > management software to query possible slots where devices can be
>> >> > plugged.
>> >> >
>> >> > This implementation of the command will return:
>> >> >
>> >> > * Multiple PCI slots per bus, in the case of PCI buses;
>> >> > * One slot per bus in the case of the other buses;
>> >> 
>> >> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
>> >> slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
>> >> or two (PATA).  For more examples, see qdev-device-use.txt section
>> >> "Specifying Bus and Address on Bus".
>> >
>> > Yes, I should have clarified that: this version changes only PCI
>> > to expose multiple slots, but other buses also need be changed to
>> > implement BusClass::enumerate_slots() properly, too.
>> >
>> >> 
>> >> > * One slot for each entry from query-hotpluggable-cpus.
>> >> > In the example below, I am not sure if the PCIe ports are all
>> >> > supposed to report all slots, but I didn't find any existing
>> >> > field in PCIBus that would help me figure out the actual number
>> >> > of slots in a given PCI bus.
>> >> >
>> >> > Git tree
>> >> > --------
>> >> >
>> >> > This patch needs the previous query-machines series I am working
>> >> > on. The full tree can be found on the git tree at:
>> >> >
>> >> >   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
>> >> >
>> >> > Example output
>> >> > --------------
>> >> >
>> >> > The following output was returned by QEMU when running it as:
>> >> >
>> >> >  $ qemu-system-x86_64 -machine q35 \
>> >> >    -readconfig docs/q35-chipset.cfg \
>> >> >    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
>> >> >
>> >> >  {
>> >> >     "return": [
>> >> 
>> >> Are you sure >3000 lines of example output make sense here?
>> >
>> > I'm not. :)
>> >
>> > That's why I need feedback from the PCI experts. I believe most
>> > of the PCI ports on q35 accept only one device, but I see no code
>> > implementing that restriction, and no obvious PCIBus or
>> > PCIBusClass field indicating that.
>> >
>> >> 
>> >> [...]
>> >> >     ]
>> >> >   }
>> >> >
>> >> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
>> >> > ---
>> >> >  qapi-schema.json       | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
>> >> >  include/hw/qdev-core.h |   6 +++
>> >> >  hw/core/bus.c          |  49 +++++++++++++++++++++
>> >> >  hw/pci/pci.c           | 106 +++++++++++++++++++++++++++++++++------------
>> >> >  qdev-monitor.c         |  86 ++++++++++++++++++++++++++++++++++---
>> >> >  5 files changed, 328 insertions(+), 33 deletions(-)
>> >> >
>> >> > diff --git a/qapi-schema.json b/qapi-schema.json
>> >> > index d48ff3f..484d91e 100644
>> >> > --- a/qapi-schema.json
>> >> > +++ b/qapi-schema.json
>> >> > @@ -3166,6 +3166,120 @@
>> >> >  { 'command': 'closefd', 'data': {'fdname': 'str'} }
>> >> >  
>> >> >  ##
>> >> > +# @DeviceSlotType:
>> >> > +#
>> >> > +# Type of device slot
>> >> > +#
>> >> > +# @generic: Generic device slot, with no bus-specific information
>> >> > +# @pci: PCI device slot
>> >> > +# @cpu: CPU device slot
>> >> > +##
>> >> > +{ 'enum': 'DeviceSlotType',
>> >> > +  'data': ['generic', 'pci', 'cpu'] }
>> >> > +
>> >> > +##
>> >> > +# @DeviceSlotInfo:
>> >> > +#
>> >> > +# Information on a slot where devices can be plugged. Some buses
>> >> > +# are represented as a single slot that can support multiple devices,
>> >> > +# and some buses have multiple slots that are identified by arguments
>> >> > +# to @device_add.
>> >> 
>> >> Okay, let me try to wrap my poor, ignorant mind around this.
>> >> 
>> >> There are two kinds of buses: "single slot that can support multiple
>> >> devices", and "multiple slots that are identified by arguments".
>> >> 
>> >> How are the two kinds related to @type?
>> >
>> > They are related to @type indirectly because different bus types
>> > will return different data. But I don't want to make this part of
>> > the specification: clients should be prepared to handle both
>> > cases.
>> 
>> Well, color me confused :)
>> 
>> > e.g. a QEMU version might return a single generic catch-all
>> > sysbus-device slot that support any number of devices. Future
>> > versions may return more detailed information, and return slots
>> > only for the sysbus devices that really work with the machine.
>> 
>> Hmm.  See below.
>> 
>> >> 
>> >> Examples for "multiple slots that are identified by arguments":
>> >> 
>> >>     -device edu,addr=06.0,bus=...
>> >>     -device scsi-hd,scsi-hd=5,bus=...
>> >>     -device ide-hd,unit=1,bus=...
>> >>     -device virtserialport,nr=7,bus=...
>> >> 
>> >> Note that each of these buses has its own way to specify a slot address,
>> >> namely a bus-specific option.
>> >
>> > Yes.
>> >
>> >> 
>> >> Can you give examples for "single slot that can support multiple
>> >> devices"?
>> >
>> > I can't name any example except sysbus, right now.
>> 
>> Sysbus is a bad example, because it's a hack, not a bus.
>> 
>> Physical devices are wired up in some device-specific way.  An important
>> special wiring case is plugging into a standard socket provided by a
>> bus.  But not every device is connected only to a bus.  Devices can also
>> be wired to other devices in ad hoc ways.
>> 
>> In the initial qdev design, devices could only plug into buses.
>> Anything that didn't fit the mold was declared to plug into the "system
>> bus", which isn't a bus at all.
>> 
>> The hack used to be fairly harmless, because you couldn't do much with
>> system bus devices anyway.  Not true anymore: Alex created means to add
>> certain system bus devices to certain machines with -device.  Has been a
>> thorn in my side ever since.  I'm afraid we need to understand how
>> exactly it works before we can finalize the design for this command.
>
> I agree it is a hack, I just wanted to be able to cover it too
> (and possibly other cases where there's no obvious "address"
> parameter to devices).
>
> The other options I had were:
> a) Omitting the bus from the output;
> b) Waiting until we fix sysbus before implementing the interface.
>
> I avoided (a) because I would like to tell libvirt "always check
> query-device-slots before plugging anything. if it's not there,
> it means you can't plug it". To do that, I would need to ensure
> all buses are present in the list (even sysbus).
>
> (But as I say below: I don't love this solution and I am open to
> suggestions).

I'm okay with doing (c) including all buses, with full information for
only some.  As long as the case "no full information" is clearly
separate, and appropriately documented.  Then we can tell libvirt to
always check query-device-slots, and if it has full information, use
that.  Else, falling back to hardcoded strategies is okay, similar to
what's done when command query-device-slots doesn't exist.

>> > There are two cases that I tried to cover by reporting
>> > multiple-device no-argument slots:
>> >
>> > 1) Buses that were not converted (yet) to implement
>> >    enumerate_buses() (in the current version: everything except
>> >    PCI)
>> > 2) Buses that really do not require any extra slot address
>> >    argument (sysbus? others?)
>> >
>> > I'm proposing this interface because I don't want (1) or (2) to
>> > be missing from the returned data (otherwise management software
>> > can't differentiate "this bus is not available" from "this bus
>> > simply doesn't implement slot enumeration yet").
>> >
>> > We won't need the special multi-device-slot case if we eliminate
>> > all instances of (1) and (2). Can we do that in 2.9? I'm not
>> > sure.
>> 
>> If we can't do a complete job, and we don't want to hide slots affected
>> by that, the data we return for them should unequivocally state that
>> it's incomplete because computing complete data hasn't been implemented,
>> yet.
>
> OK, so we agree we need a way to tell the client the data is
> incomplete (or not as detailed as we would like).

Yes.

> I tried to solve that by providing a mechanism to tell the client
> "look, I can tell you that there's a bus called <bus name>, and
> it is valid to use it as the 'bus' argument for -device and
> device_add, but I don't know the rest of the rules (sorry!)". We
> can do that by simply including the bus on the list as if it was
> a multi-device slot.

What if we ever run into a kind of slot where the full information is
"use this 'bus' argument" and "you can plug in multiple devices"?
Wouldn't that be indistinguishable from "full information isn't
available"?

Let's make "full information isn't available" impossible to confuse with
any conceivable set of full information.

> But I don't love this solution, so I am open to other suggestions
> on how to tell the client that slot information is incomplete for
> a bus.
>
> Maybe the following?
>
> 1) Add a new DeviceSlotType value meaning "this entry represents
>    a bus/device-type that can't enumerate its slots" (let's call
>    it "bus-with-no-slot-info" by now).

Let's call this one a non-slot, for brevity.

> 2) Remove DeviceSlotType "generic".
> 3) Remove max-devices field, and simply document the device-limit
>    semantics for each DeviceSlotType (cpu: 1 device, pci: 1
>    device, bus-with-no-slot-info: undefined limit).

Not sure there's much to document.  For me a slot takes exactly one plug
by definition (if it can take more, it's a set of slots, isn't it?), and
non-slots take an unknown number of plugs by definition (if we knew,
we'd return suitable slots instead).  But that's mincing words; I'm sure
we can come up with language that works for both of us.

> This looks like a trade-off between moving data to specific union
> types (and representing type-specific limits/rules in the schema
> documentation), or moving data to the base union type (and
> representing type-specific limits as data in the base union
> type).

Works for me.

Additionally, let's replace 'devices': [ 'str' ] by '*device': 'str',
because a slot is either empty or has one device plugged in.

If we ever need to add a set-of-slots type, we'll have to extend the
schema, but then we'll be in a much better position to know what we
actually need.

>> >> > +#
>> >> > +# @bus: ID of the bus object where the device can be plugged. Optional,
>> >> > +#       as some devices don't need a bus to be plugged (e.g. CPUs).
>> >> > +#       Can be copied to the "bus" argument to @device_add.
>> >> 
>> >> Suggest something like "Can be used as value for @device_add's bus
>> >> option".
>> >
>> > Will do.
>> >
>> >> 
>> >> Should we give similar information on the slot address?  The option name
>> >> is obvious.  What about acceptable values?
>> >
>> > Actually, this part of the documentation was a leftover from a
>> > previous version where @bus was inside DeviceSlotInfo. Now I
>> > moved @bus inside @props for all device types, and it should be
>> > documented the same way as the other fields inside @props.
>> >
>> >> 
>> >> > +#
>> >> > +# @type: type of device slot.
>> >> > +#
>> >> > +# @accepted-device-types: List of device types accepted by the slot.
>> >> > +#                         Any device plugged to the slot should implement
>> >> > +#                         one of the accepted device types.
>> >> > +#
>> >> > +# @max-devices: #optional maximum number of devices that can be plugged
>> >> > +#               to the slot.
>> >> 
>> >> What does it mean when @max-devices isn't given?
>> >
>> > It means there is no hard limit on the number of pluggable
>> > devices. sysbus is one of such cases.
>> 
>> Sysbus certainly has limits, but they're more complicated than just "at
>> most @max-devices devices".
>> 
>> >> > +#
>> >> > +# @devices: List of QOM paths for devices that are already plugged.
>> >> > +#
>> >> > +# @available: If false, the slot is not available for plugging any device.
>> >> > +#             This value can change at runtime if condition changes
>> >> > +#             (e.g. if the device becomes full, or if the machine
>> >> > +#             was already initialized and the slot doesn't support
>> >> > +#             hotplug).
>> >> > +#
>> >> > +# @hotpluggable: If true, the slot accepts hotplugged devices.
>> >> > +#
>> >> > +# DeviceSlotInfo structs always have a @props member, whose members
>> >> > +# can be directly copied to the arguments to @device_add.
>> >> 
>> >> Do you mean names of properties common to all @accepted-device-types?
>> >
>> > I mean that it should be safe to simply use the keys+values in
>> > @props as arguments to device_add or -device, for any slot @type.
>> > Some clients may want to extract some information from @props (if
>> > they already manage PCI addresses, for example), but some of them
>> > may simply choose any available slot and copy @props blindly.
>> >
>> > I didn't want to state anything about QOM properties, because I
>> > only want @props to represent device_add/-device arguments. Some
>> > of those arguments are consumed by the device_add code itself
>> > (e.g. "bus") and others are translated to QOM properties. I don't
>> > want the interface to impose any restrictions on how this is
>> > implemented.
>> 
>> Hmm, is this meant to work as follows?  To plug a device into this slot,
>> you use
>> 
>>     { "execute": "device_add",
>>       "arguments": { "driver": TYPE, ... PROPS ... }
>> 
>> where TYPE is a (concrete subtype of a) member of
>> @accepted-device-types, and PROPS is the value of @props spliced in.
>> Example: the data for slot 06.0 of PCI bus pci.0 would be something like
>> 
>>     {
>>       "bus": "pci.0",
>
> There's no "bus" field in DeviceSlotInfo. It was a documentation bug.

Should read code, not docs ;)

>>       "props": {
>>           "bus": "pci.0",
>>           "addr": "06.0"
>>       }
>>     }
>> 
>> Doesn't match your example output; I guess I'm still misunderstanding
>> something.
>
> This is a busy PCI slot, from my example:
>
>         {
>             "available": false,
>             "devices": [
>                 "/machine/q35/mch"
>             ],
>             "accepted-device-types": [
>                 "legacy-pci-device",
>                 "pci-express-device"
>             ],
>             "props": {
>                 "bus": "/machine/q35/pcie.0",

Does device_add device_add bus=/machine/q35/pcie.0 even work?  I know
qbus_find() interprets /-separated paths, but their semantics are FUBAR
(I can find details if you're curious).  We've long advised to use only
values without '/', like bus=pcie.0.

Now, QOM gives us a framework for interpreting paths sanely.  Switching
the value of bus to QOM paths would be a compatibility break, though.
Matters only if the botched /-paths are actually used.

>                 "addr": 0
>             },
>             "hotpluggable": false,
>             "type": "pci",
>             "max-devices": 1
>         },
>
> This is an empty PCI slot from my example. It has available=false because
> hotplug is not supported on the root bus:
>
>         {
>             "available": false,
>             "devices": [],
>             "accepted-device-types": [
>                 "legacy-pci-device",
>                 "pci-express-device"  
>             ],
>             "props": {
>                 "bus": "/machine/q35/pcie.0",
>                 "addr": 32
>             },
>             "hotpluggable": false,
>             "type": "pci",
>             "max-devices": 1
>         },
>
> This is an empty PCI slot on pc_piix (not on my example):
>
>         {
>             "available": true,
>             "devices": [],
>             "accepted-device-types": [
>                 "legacy-pci-device"
>             ],
>             "props": {
>                 "bus": "/machine/i440fx/pci.0",
>                 "addr": 32
>             },
>             "hotpluggable": true,
>             "type": "pci",
>             "max-devices": 1
>         },
>
> This is a free PCIe slot, from my example:
>
>         {
>             "available": true,
>             "devices": [],
>             "accepted-device-types": [
>                 "pci-express-device"
>             ],
>             "props": {
>                 "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
>                 "addr": 0
>             },
>             "hotpluggable": true,
>             "type": "pci",
>             "max-devices": 1
>         },
>
> Note that it returns the uint32 value of "addr" (there's some magic on the
> devfn setter to make it accept both "<slot>[.<function>]" strings and <devfn>
> integers. On the next version, I plan to fix this insanity and replace "addr"
> with proper "slot" and "function" integer properties. (See FIXME below)
>
>> 
>> >
>> >> 
>> >> > +##
>> >> > +{ 'union': 'DeviceSlotInfo',
>> >> > +  'base': { 'type': 'DeviceSlotType',
>> >> > +            'accepted-device-types': [ 'str' ],
>> >> > +            '*max-devices': 'int', 'devices': [ 'str' ],
>> >> > +            'available': 'bool', 'hotpluggable': 'bool' },
>> >> > +  'discriminator': 'type',
>> >> > +  'data': { 'generic': 'GenericSlotInfo',
>> >> > +            'pci': 'PCISlotInfo',
>> >> > +            'cpu': 'CPUSlotInfo' } }
>> >> > +
>> >> > +##
>> >> > +# @GenericSlotProperties:
>> >> > +##
>> >> > +{ 'struct': 'GenericSlotProperties',
>> >> > +  'data': { 'bus': 'str' } }
>> >> > +
>> >> > +
>> >> > +##
>> >> > +# @GenericSlotInfo:
>> >> > +#
>> >> > +# Generic slot information, with no bus-specific information
>> >> > +##
>> >> > +{ 'struct': 'GenericSlotInfo',
>> >> > +  'data': { 'props': 'GenericSlotProperties' } }
>> >> > +
>> >> > +##
>> >> > +# @PCIDeviceSlotProperties:
>> >> > +#
>> >> > +# Properties that can be set when plugging a PCI device.
>> >> > +#
>> >> > +# @addr: "addr" argument to @device_add.
>> >> > +#
>> >> > +#FIXME: replace @addr with slot and function properties.
>> >> > +##
>> >> > +{ 'struct': 'PCIDeviceSlotProperties',
>> >> > +  'data': { 'bus': 'str', 'addr': 'int' } }
>> >> > +
>> >> > +##
>> >> > +# @PCISlotInfo:
>> >> > +#
>> >> > +# Information on a PCI device slot.
>> >> > +#
>> >> > +# @props: The @device_add arguments that can be used when plugging
>> >> > +# the device.
>> >> > +##
>> >> > +{ 'struct': 'PCISlotInfo',
>> >> > +  'data': { 'props': 'PCIDeviceSlotProperties' } }
>> >> > +
>> >> > +##
>> >> > +# @CPUSlotInfo:
>> >> > +#
>> >> > +# Information on a CPU device slot.
>> >> > +#
>> >> > +# @props: The @device_add arguments that can be used when plugging
>> >> > +# the device.
>> >> > +##
>> >> > +{ 'struct': 'CPUSlotInfo',
>> >> > +  'data': { 'props': 'CpuInstanceProperties' } }
>> >> > +
>> >> > +##
>> >> > +# @query-device-slots:
>> >> > +#
>> >> > +# Return the list of possible slots for plugging devices using
>> >> > +# @device_add.
>> >> > +##
>> >> > +{ 'command': 'query-device-slots',
>> >> > +  'returns': [ 'DeviceSlotInfo' ] }
>> >> > +
>> >> > +##
>> >> >  # @MachineBusInfo
>> >> >  #
>> >> >  # Information about a bus present on a machine.
>> >> [...]
Eduardo Habkost Dec. 14, 2016, 4:46 p.m. UTC | #8
On Wed, Dec 14, 2016 at 04:34:04PM +0100, Markus Armbruster wrote:
> Eduardo Habkost <ehabkost@redhat.com> writes:
> 
> > On Tue, Dec 13, 2016 at 08:51:34PM +0100, Markus Armbruster wrote:
> >> Eduardo Habkost <ehabkost@redhat.com> writes:
> >> 
> >> > On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
> >> >> Quick interface review only:
> >> >> 
> >> >> Eduardo Habkost <ehabkost@redhat.com> writes:
> >> >> 
> >> >> > This adds a new command to QMP: query-device-slots. It will allow
> >> >> > management software to query possible slots where devices can be
> >> >> > plugged.
> >> >> >
> >> >> > This implementation of the command will return:
> >> >> >
> >> >> > * Multiple PCI slots per bus, in the case of PCI buses;
> >> >> > * One slot per bus in the case of the other buses;
> >> >> 
> >> >> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
> >> >> slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
> >> >> or two (PATA).  For more examples, see qdev-device-use.txt section
> >> >> "Specifying Bus and Address on Bus".
> >> >
> >> > Yes, I should have clarified that: this version changes only PCI
> >> > to expose multiple slots, but other buses also need be changed to
> >> > implement BusClass::enumerate_slots() properly, too.
> >> >
> >> >> 
> >> >> > * One slot for each entry from query-hotpluggable-cpus.
> >> >> > In the example below, I am not sure if the PCIe ports are all
> >> >> > supposed to report all slots, but I didn't find any existing
> >> >> > field in PCIBus that would help me figure out the actual number
> >> >> > of slots in a given PCI bus.
> >> >> >
> >> >> > Git tree
> >> >> > --------
> >> >> >
> >> >> > This patch needs the previous query-machines series I am working
> >> >> > on. The full tree can be found on the git tree at:
> >> >> >
> >> >> >   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
> >> >> >
> >> >> > Example output
> >> >> > --------------
> >> >> >
> >> >> > The following output was returned by QEMU when running it as:
> >> >> >
> >> >> >  $ qemu-system-x86_64 -machine q35 \
> >> >> >    -readconfig docs/q35-chipset.cfg \
> >> >> >    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
> >> >> >
> >> >> >  {
> >> >> >     "return": [
> >> >> 
> >> >> Are you sure >3000 lines of example output make sense here?
> >> >
> >> > I'm not. :)
> >> >
> >> > That's why I need feedback from the PCI experts. I believe most
> >> > of the PCI ports on q35 accept only one device, but I see no code
> >> > implementing that restriction, and no obvious PCIBus or
> >> > PCIBusClass field indicating that.
> >> >
> >> >> 
> >> >> [...]
> >> >> >     ]
> >> >> >   }
> >> >> >
> >> >> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> >> >> > ---
> >> >> >  qapi-schema.json       | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
> >> >> >  include/hw/qdev-core.h |   6 +++
> >> >> >  hw/core/bus.c          |  49 +++++++++++++++++++++
> >> >> >  hw/pci/pci.c           | 106 +++++++++++++++++++++++++++++++++------------
> >> >> >  qdev-monitor.c         |  86 ++++++++++++++++++++++++++++++++++---
> >> >> >  5 files changed, 328 insertions(+), 33 deletions(-)
> >> >> >
> >> >> > diff --git a/qapi-schema.json b/qapi-schema.json
> >> >> > index d48ff3f..484d91e 100644
> >> >> > --- a/qapi-schema.json
> >> >> > +++ b/qapi-schema.json
> >> >> > @@ -3166,6 +3166,120 @@
> >> >> >  { 'command': 'closefd', 'data': {'fdname': 'str'} }
> >> >> >  
> >> >> >  ##
> >> >> > +# @DeviceSlotType:
> >> >> > +#
> >> >> > +# Type of device slot
> >> >> > +#
> >> >> > +# @generic: Generic device slot, with no bus-specific information
> >> >> > +# @pci: PCI device slot
> >> >> > +# @cpu: CPU device slot
> >> >> > +##
> >> >> > +{ 'enum': 'DeviceSlotType',
> >> >> > +  'data': ['generic', 'pci', 'cpu'] }
> >> >> > +
> >> >> > +##
> >> >> > +# @DeviceSlotInfo:
> >> >> > +#
> >> >> > +# Information on a slot where devices can be plugged. Some buses
> >> >> > +# are represented as a single slot that can support multiple devices,
> >> >> > +# and some buses have multiple slots that are identified by arguments
> >> >> > +# to @device_add.
> >> >> 
> >> >> Okay, let me try to wrap my poor, ignorant mind around this.
> >> >> 
> >> >> There are two kinds of buses: "single slot that can support multiple
> >> >> devices", and "multiple slots that are identified by arguments".
> >> >> 
> >> >> How are the two kinds related to @type?
> >> >
> >> > They are related to @type indirectly because different bus types
> >> > will return different data. But I don't want to make this part of
> >> > the specification: clients should be prepared to handle both
> >> > cases.
> >> 
> >> Well, color me confused :)
> >> 
> >> > e.g. a QEMU version might return a single generic catch-all
> >> > sysbus-device slot that support any number of devices. Future
> >> > versions may return more detailed information, and return slots
> >> > only for the sysbus devices that really work with the machine.
> >> 
> >> Hmm.  See below.
> >> 
> >> >> 
> >> >> Examples for "multiple slots that are identified by arguments":
> >> >> 
> >> >>     -device edu,addr=06.0,bus=...
> >> >>     -device scsi-hd,scsi-hd=5,bus=...
> >> >>     -device ide-hd,unit=1,bus=...
> >> >>     -device virtserialport,nr=7,bus=...
> >> >> 
> >> >> Note that each of these buses has its own way to specify a slot address,
> >> >> namely a bus-specific option.
> >> >
> >> > Yes.
> >> >
> >> >> 
> >> >> Can you give examples for "single slot that can support multiple
> >> >> devices"?
> >> >
> >> > I can't name any example except sysbus, right now.
> >> 
> >> Sysbus is a bad example, because it's a hack, not a bus.
> >> 
> >> Physical devices are wired up in some device-specific way.  An important
> >> special wiring case is plugging into a standard socket provided by a
> >> bus.  But not every device is connected only to a bus.  Devices can also
> >> be wired to other devices in ad hoc ways.
> >> 
> >> In the initial qdev design, devices could only plug into buses.
> >> Anything that didn't fit the mold was declared to plug into the "system
> >> bus", which isn't a bus at all.
> >> 
> >> The hack used to be fairly harmless, because you couldn't do much with
> >> system bus devices anyway.  Not true anymore: Alex created means to add
> >> certain system bus devices to certain machines with -device.  Has been a
> >> thorn in my side ever since.  I'm afraid we need to understand how
> >> exactly it works before we can finalize the design for this command.
> >
> > I agree it is a hack, I just wanted to be able to cover it too
> > (and possibly other cases where there's no obvious "address"
> > parameter to devices).
> >
> > The other options I had were:
> > a) Omitting the bus from the output;
> > b) Waiting until we fix sysbus before implementing the interface.
> >
> > I avoided (a) because I would like to tell libvirt "always check
> > query-device-slots before plugging anything. if it's not there,
> > it means you can't plug it". To do that, I would need to ensure
> > all buses are present in the list (even sysbus).
> >
> > (But as I say below: I don't love this solution and I am open to
> > suggestions).
> 
> I'm okay with doing (c) including all buses, with full information for
> only some.  As long as the case "no full information" is clearly
> separate, and appropriately documented.  Then we can tell libvirt to
> always check query-device-slots, and if it has full information, use
> that.  Else, falling back to hardcoded strategies is okay, similar to
> what's done when command query-device-slots doesn't exist.

Understood.

> 
> >> > There are two cases that I tried to cover by reporting
> >> > multiple-device no-argument slots:
> >> >
> >> > 1) Buses that were not converted (yet) to implement
> >> >    enumerate_buses() (in the current version: everything except
> >> >    PCI)
> >> > 2) Buses that really do not require any extra slot address
> >> >    argument (sysbus? others?)
> >> >
> >> > I'm proposing this interface because I don't want (1) or (2) to
> >> > be missing from the returned data (otherwise management software
> >> > can't differentiate "this bus is not available" from "this bus
> >> > simply doesn't implement slot enumeration yet").
> >> >
> >> > We won't need the special multi-device-slot case if we eliminate
> >> > all instances of (1) and (2). Can we do that in 2.9? I'm not
> >> > sure.
> >> 
> >> If we can't do a complete job, and we don't want to hide slots affected
> >> by that, the data we return for them should unequivocally state that
> >> it's incomplete because computing complete data hasn't been implemented,
> >> yet.
> >
> > OK, so we agree we need a way to tell the client the data is
> > incomplete (or not as detailed as we would like).
> 
> Yes.
> 
> > I tried to solve that by providing a mechanism to tell the client
> > "look, I can tell you that there's a bus called <bus name>, and
> > it is valid to use it as the 'bus' argument for -device and
> > device_add, but I don't know the rest of the rules (sorry!)". We
> > can do that by simply including the bus on the list as if it was
> > a multi-device slot.
> 
> What if we ever run into a kind of slot where the full information is
> "use this 'bus' argument" and "you can plug in multiple devices"?
> Wouldn't that be indistinguishable from "full information isn't
> available"?
> 
> Let's make "full information isn't available" impossible to confuse with
> any conceivable set of full information.

I see. I tried to model it in a way that "incomplete information"
was not considered an exception, just an instance where the data
is less informative. But adding another bit of information that
explicitly says "this in incomplete and will probably change in
the future" is a good idea.

> 
> > But I don't love this solution, so I am open to other suggestions
> > on how to tell the client that slot information is incomplete for
> > a bus.
> >
> > Maybe the following?
> >
> > 1) Add a new DeviceSlotType value meaning "this entry represents
> >    a bus/device-type that can't enumerate its slots" (let's call
> >    it "bus-with-no-slot-info" by now).
> 
> Let's call this one a non-slot, for brevity.

I like that name. :)

> 
> > 2) Remove DeviceSlotType "generic".
> > 3) Remove max-devices field, and simply document the device-limit
> >    semantics for each DeviceSlotType (cpu: 1 device, pci: 1
> >    device, bus-with-no-slot-info: undefined limit).
> 
> Not sure there's much to document.  For me a slot takes exactly one plug
> by definition (if it can take more, it's a set of slots, isn't it?), and
> non-slots take an unknown number of plugs by definition (if we knew,
> we'd return suitable slots instead).  But that's mincing words; I'm sure
> we can come up with language that works for both of us.

I am trying to not make any guarantees about all slot types,
until we learn more about the more complex cases. All we can say
right now is that CPU and PCI slots support only 1 device, and we
don't claim anything about others.

I'm not even 100% sure about the 1-device rule for PCI, as each
PCI function in a slot is a separate DeviceState*. I need to
understand how multi-function PCI hotplug works, to find out what
we should do.

I worry about having a general 1-slot = 1-device rule from the
beginning, because I don't know if it can be a problem when the
set of possible slots is too large. e.g.: should we return one
slot for each of the 256 possible devfns in a PCI bus? What if a
bus uses 32-bit identifiers for devices?

My suggestion is to avoid any general claims about device limits
in slots in the documentation by now, until we have more
knowledge about other bus types.

> 
> > This looks like a trade-off between moving data to specific union
> > types (and representing type-specific limits/rules in the schema
> > documentation), or moving data to the base union type (and
> > representing type-specific limits as data in the base union
> > type).
> 
> Works for me.
> 
> Additionally, let's replace 'devices': [ 'str' ] by '*device': 'str',
> because a slot is either empty or has one device plugged in.
> 
> If we ever need to add a set-of-slots type, we'll have to extend the
> schema, but then we'll be in a much better position to know what we
> actually need.
> 

I am working on a version that even removes 'devices', for
simplicity. Then we would have more freedom when extending the
schema later.


> >> >> > +#
> >> >> > +# @bus: ID of the bus object where the device can be plugged. Optional,
> >> >> > +#       as some devices don't need a bus to be plugged (e.g. CPUs).
> >> >> > +#       Can be copied to the "bus" argument to @device_add.
> >> >> 
> >> >> Suggest something like "Can be used as value for @device_add's bus
> >> >> option".
> >> >
> >> > Will do.
> >> >
> >> >> 
> >> >> Should we give similar information on the slot address?  The option name
> >> >> is obvious.  What about acceptable values?
> >> >
> >> > Actually, this part of the documentation was a leftover from a
> >> > previous version where @bus was inside DeviceSlotInfo. Now I
> >> > moved @bus inside @props for all device types, and it should be
> >> > documented the same way as the other fields inside @props.
> >> >
> >> >> 
> >> >> > +#
> >> >> > +# @type: type of device slot.
> >> >> > +#
> >> >> > +# @accepted-device-types: List of device types accepted by the slot.
> >> >> > +#                         Any device plugged to the slot should implement
> >> >> > +#                         one of the accepted device types.
> >> >> > +#
> >> >> > +# @max-devices: #optional maximum number of devices that can be plugged
> >> >> > +#               to the slot.
> >> >> 
> >> >> What does it mean when @max-devices isn't given?
> >> >
> >> > It means there is no hard limit on the number of pluggable
> >> > devices. sysbus is one of such cases.
> >> 
> >> Sysbus certainly has limits, but they're more complicated than just "at
> >> most @max-devices devices".
> >> 
> >> >> > +#
> >> >> > +# @devices: List of QOM paths for devices that are already plugged.
> >> >> > +#
> >> >> > +# @available: If false, the slot is not available for plugging any device.
> >> >> > +#             This value can change at runtime if condition changes
> >> >> > +#             (e.g. if the device becomes full, or if the machine
> >> >> > +#             was already initialized and the slot doesn't support
> >> >> > +#             hotplug).
> >> >> > +#
> >> >> > +# @hotpluggable: If true, the slot accepts hotplugged devices.
> >> >> > +#
> >> >> > +# DeviceSlotInfo structs always have a @props member, whose members
> >> >> > +# can be directly copied to the arguments to @device_add.
> >> >> 
> >> >> Do you mean names of properties common to all @accepted-device-types?
> >> >
> >> > I mean that it should be safe to simply use the keys+values in
> >> > @props as arguments to device_add or -device, for any slot @type.
> >> > Some clients may want to extract some information from @props (if
> >> > they already manage PCI addresses, for example), but some of them
> >> > may simply choose any available slot and copy @props blindly.
> >> >
> >> > I didn't want to state anything about QOM properties, because I
> >> > only want @props to represent device_add/-device arguments. Some
> >> > of those arguments are consumed by the device_add code itself
> >> > (e.g. "bus") and others are translated to QOM properties. I don't
> >> > want the interface to impose any restrictions on how this is
> >> > implemented.
> >> 
> >> Hmm, is this meant to work as follows?  To plug a device into this slot,
> >> you use
> >> 
> >>     { "execute": "device_add",
> >>       "arguments": { "driver": TYPE, ... PROPS ... }
> >> 
> >> where TYPE is a (concrete subtype of a) member of
> >> @accepted-device-types, and PROPS is the value of @props spliced in.
> >> Example: the data for slot 06.0 of PCI bus pci.0 would be something like
> >> 
> >>     {
> >>       "bus": "pci.0",
> >
> > There's no "bus" field in DeviceSlotInfo. It was a documentation bug.
> 
> Should read code, not docs ;)
> 
> >>       "props": {
> >>           "bus": "pci.0",
> >>           "addr": "06.0"
> >>       }
> >>     }
> >> 
> >> Doesn't match your example output; I guess I'm still misunderstanding
> >> something.
> >
> > This is a busy PCI slot, from my example:
> >
> >         {
> >             "available": false,
> >             "devices": [
> >                 "/machine/q35/mch"
> >             ],
> >             "accepted-device-types": [
> >                 "legacy-pci-device",
> >                 "pci-express-device"
> >             ],
> >             "props": {
> >                 "bus": "/machine/q35/pcie.0",
> 
> Does device_add device_add bus=/machine/q35/pcie.0 even work?  I know
> qbus_find() interprets /-separated paths, but their semantics are FUBAR
> (I can find details if you're curious).  We've long advised to use only
> values without '/', like bus=pcie.0.

@device_add documentation says:

# @bus: #optional the device's parent bus (device tree path)

> 
> Now, QOM gives us a framework for interpreting paths sanely.  Switching
> the value of bus to QOM paths would be a compatibility break, though.
> Matters only if the botched /-paths are actually used.

I would be happy to use the bus name instead of the full path,
but I don't see any code that ensures that bus names are really
unique. Is there any existing mechanism to ensure that?

> 
> >                 "addr": 0
> >             },
> >             "hotpluggable": false,
> >             "type": "pci",
> >             "max-devices": 1
> >         },
> >
> > This is an empty PCI slot from my example. It has available=false because
> > hotplug is not supported on the root bus:
> >
> >         {
> >             "available": false,
> >             "devices": [],
> >             "accepted-device-types": [
> >                 "legacy-pci-device",
> >                 "pci-express-device"  
> >             ],
> >             "props": {
> >                 "bus": "/machine/q35/pcie.0",
> >                 "addr": 32
> >             },
> >             "hotpluggable": false,
> >             "type": "pci",
> >             "max-devices": 1
> >         },
> >
> > This is an empty PCI slot on pc_piix (not on my example):
> >
> >         {
> >             "available": true,
> >             "devices": [],
> >             "accepted-device-types": [
> >                 "legacy-pci-device"
> >             ],
> >             "props": {
> >                 "bus": "/machine/i440fx/pci.0",
> >                 "addr": 32
> >             },
> >             "hotpluggable": true,
> >             "type": "pci",
> >             "max-devices": 1
> >         },
> >
> > This is a free PCIe slot, from my example:
> >
> >         {
> >             "available": true,
> >             "devices": [],
> >             "accepted-device-types": [
> >                 "pci-express-device"
> >             ],
> >             "props": {
> >                 "bus": "/machine/peripheral/ich9-pcie-port-3/ich9-pcie-port-3",
> >                 "addr": 0
> >             },
> >             "hotpluggable": true,
> >             "type": "pci",
> >             "max-devices": 1
> >         },
> >
> > Note that it returns the uint32 value of "addr" (there's some magic on the
> > devfn setter to make it accept both "<slot>[.<function>]" strings and <devfn>
> > integers. On the next version, I plan to fix this insanity and replace "addr"
> > with proper "slot" and "function" integer properties. (See FIXME below)
> >
> >> 
> >> >
> >> >> 
> >> >> > +##
> >> >> > +{ 'union': 'DeviceSlotInfo',
> >> >> > +  'base': { 'type': 'DeviceSlotType',
> >> >> > +            'accepted-device-types': [ 'str' ],
> >> >> > +            '*max-devices': 'int', 'devices': [ 'str' ],
> >> >> > +            'available': 'bool', 'hotpluggable': 'bool' },
> >> >> > +  'discriminator': 'type',
> >> >> > +  'data': { 'generic': 'GenericSlotInfo',
> >> >> > +            'pci': 'PCISlotInfo',
> >> >> > +            'cpu': 'CPUSlotInfo' } }
> >> >> > +
> >> >> > +##
> >> >> > +# @GenericSlotProperties:
> >> >> > +##
> >> >> > +{ 'struct': 'GenericSlotProperties',
> >> >> > +  'data': { 'bus': 'str' } }
> >> >> > +
> >> >> > +
> >> >> > +##
> >> >> > +# @GenericSlotInfo:
> >> >> > +#
> >> >> > +# Generic slot information, with no bus-specific information
> >> >> > +##
> >> >> > +{ 'struct': 'GenericSlotInfo',
> >> >> > +  'data': { 'props': 'GenericSlotProperties' } }
> >> >> > +
> >> >> > +##
> >> >> > +# @PCIDeviceSlotProperties:
> >> >> > +#
> >> >> > +# Properties that can be set when plugging a PCI device.
> >> >> > +#
> >> >> > +# @addr: "addr" argument to @device_add.
> >> >> > +#
> >> >> > +#FIXME: replace @addr with slot and function properties.
> >> >> > +##
> >> >> > +{ 'struct': 'PCIDeviceSlotProperties',
> >> >> > +  'data': { 'bus': 'str', 'addr': 'int' } }
> >> >> > +
> >> >> > +##
> >> >> > +# @PCISlotInfo:
> >> >> > +#
> >> >> > +# Information on a PCI device slot.
> >> >> > +#
> >> >> > +# @props: The @device_add arguments that can be used when plugging
> >> >> > +# the device.
> >> >> > +##
> >> >> > +{ 'struct': 'PCISlotInfo',
> >> >> > +  'data': { 'props': 'PCIDeviceSlotProperties' } }
> >> >> > +
> >> >> > +##
> >> >> > +# @CPUSlotInfo:
> >> >> > +#
> >> >> > +# Information on a CPU device slot.
> >> >> > +#
> >> >> > +# @props: The @device_add arguments that can be used when plugging
> >> >> > +# the device.
> >> >> > +##
> >> >> > +{ 'struct': 'CPUSlotInfo',
> >> >> > +  'data': { 'props': 'CpuInstanceProperties' } }
> >> >> > +
> >> >> > +##
> >> >> > +# @query-device-slots:
> >> >> > +#
> >> >> > +# Return the list of possible slots for plugging devices using
> >> >> > +# @device_add.
> >> >> > +##
> >> >> > +{ 'command': 'query-device-slots',
> >> >> > +  'returns': [ 'DeviceSlotInfo' ] }
> >> >> > +
> >> >> > +##
> >> >> >  # @MachineBusInfo
> >> >> >  #
> >> >> >  # Information about a bus present on a machine.
> >> >> [...]
Marcel Apfelbaum Dec. 14, 2016, 5:17 p.m. UTC | #9
On 12/13/2016 06:00 PM, Eduardo Habkost wrote:
> On Tue, Dec 13, 2016 at 04:15:18PM +0200, Marcel Apfelbaum wrote:
>> On 12/13/2016 02:42 PM, Eduardo Habkost wrote:
>>> On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
>>>> Quick interface review only:
>>>>
>>>> Eduardo Habkost <ehabkost@redhat.com> writes:
>>>>
>>>>> This adds a new command to QMP: query-device-slots. It will allow
>>>>> management software to query possible slots where devices can be
>>>>> plugged.
>>>>>
>>>>> This implementation of the command will return:
>>>>>
>>>>> * Multiple PCI slots per bus, in the case of PCI buses;
>>>>> * One slot per bus in the case of the other buses;
>>>>
>>>> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
>>>> slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
>>>> or two (PATA).  For more examples, see qdev-device-use.txt section
>>>> "Specifying Bus and Address on Bus".
>>>
>>> Yes, I should have clarified that: this version changes only PCI
>>> to expose multiple slots, but other buses also need be changed to
>>> implement BusClass::enumerate_slots() properly, too.
>>>
>>>>
>>>>> * One slot for each entry from query-hotpluggable-cpus.
>>>>> In the example below, I am not sure if the PCIe ports are all
>>>>> supposed to report all slots, but I didn't find any existing
>>>>> field in PCIBus that would help me figure out the actual number
>>>>> of slots in a given PCI bus.
>>>>>
>>>>> Git tree
>>>>> --------
>>>>>
>>>>> This patch needs the previous query-machines series I am working
>>>>> on. The full tree can be found on the git tree at:
>>>>>
>>>>>   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
>>>>>
>>>>> Example output
>>>>> --------------
>>>>>
>>>>> The following output was returned by QEMU when running it as:
>>>>>
>>>>>  $ qemu-system-x86_64 -machine q35 \
>>>>>    -readconfig docs/q35-chipset.cfg \
>>>>>    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
>>>>>
>>>>>  {
>>>>>     "return": [
>>>>
>>>> Are you sure >3000 lines of example output make sense here?
>>>
>>> I'm not. :)
>>>
>>> That's why I need feedback from the PCI experts. I believe most
>>> of the PCI ports on q35 accept only one device, but I see no code
>>> implementing that restriction, and no obvious PCIBus or
>>> PCIBusClass field indicating that.
>>
>> Hi,
>>
>> Sadly there is no such restriction today as far as I know.
>> We trust the user that he knows what he is doing :)
>> and we let libvirt implement the restriction.
>>
>> I do agree we should fix that.
>
> OK, this matches what I saw on the current code.
>
> But I would like to understand what exactly should be the rules
> if we fix that. Should we report only 1 slot if
> pci_bus_has_pcie_upstream_port(bus)?
>

We should report 1 slot for a bus exposed by a PCIe Root Port
or PCIe Downstream Port.

Be aware that 1 slot doesn't necessarily refer to only one device,
we can have a multi-function device.

Thanks,
Marcel

> (Note that pci_bus_has_pcie_upstream_port() doesn't exist in the
> current code, it is added by my patch)
>
Laine Stump Dec. 14, 2016, 8:10 p.m. UTC | #10
On 12/14/2016 12:17 PM, Marcel Apfelbaum wrote:
> On 12/13/2016 06:00 PM, Eduardo Habkost wrote:
>> On Tue, Dec 13, 2016 at 04:15:18PM +0200, Marcel Apfelbaum wrote:
>>> On 12/13/2016 02:42 PM, Eduardo Habkost wrote:
>>>> On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
>>>>> Quick interface review only:
>>>>>
>>>>> Eduardo Habkost <ehabkost@redhat.com> writes:
>>>>>
>>>>>> This adds a new command to QMP: query-device-slots. It will allow
>>>>>> management software to query possible slots where devices can be
>>>>>> plugged.
>>>>>>
>>>>>> This implementation of the command will return:
>>>>>>
>>>>>> * Multiple PCI slots per bus, in the case of PCI buses;
>>>>>> * One slot per bus in the case of the other buses;
>>>>>
>>>>> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
>>>>> slots.  The slot address is the SCSI ID.  An IDE bus may have one
>>>>> (SATA)
>>>>> or two (PATA).  For more examples, see qdev-device-use.txt section
>>>>> "Specifying Bus and Address on Bus".
>>>>
>>>> Yes, I should have clarified that: this version changes only PCI
>>>> to expose multiple slots, but other buses also need be changed to
>>>> implement BusClass::enumerate_slots() properly, too.
>>>>
>>>>>
>>>>>> * One slot for each entry from query-hotpluggable-cpus.
>>>>>> In the example below, I am not sure if the PCIe ports are all
>>>>>> supposed to report all slots, but I didn't find any existing
>>>>>> field in PCIBus that would help me figure out the actual number
>>>>>> of slots in a given PCI bus.
>>>>>>
>>>>>> Git tree
>>>>>> --------
>>>>>>
>>>>>> This patch needs the previous query-machines series I am working
>>>>>> on. The full tree can be found on the git tree at:
>>>>>>
>>>>>>   git://github.com/ehabkost/qemu-hacks.git
>>>>>> work/query-machines-bus-info
>>>>>>
>>>>>> Example output
>>>>>> --------------
>>>>>>
>>>>>> The following output was returned by QEMU when running it as:
>>>>>>
>>>>>>  $ qemu-system-x86_64 -machine q35 \
>>>>>>    -readconfig docs/q35-chipset.cfg \
>>>>>>    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
>>>>>>
>>>>>>  {
>>>>>>     "return": [
>>>>>
>>>>> Are you sure >3000 lines of example output make sense here?
>>>>
>>>> I'm not. :)
>>>>
>>>> That's why I need feedback from the PCI experts. I believe most
>>>> of the PCI ports on q35 accept only one device,

I don't think of it as "one device" but as "only slot 0 can be used" 
(since, as Marcel pointed out, a different device can be placed on each 
of the 8 functions of that slot).

> but I see no code
>>>> implementing that restriction, and no obvious PCIBus or
>>>> PCIBusClass field indicating that.
>>>
>>> Hi,
>>>
>>> Sadly there is no such restriction today as far as I know.
>>> We trust the user that he knows what he is doing :)
>>> and we let libvirt implement the restriction.
>>>
>>> I do agree we should fix that.
>>
>> OK, this matches what I saw on the current code.
>>
>> But I would like to understand what exactly should be the rules
>> if we fix that. Should we report only 1 slot if
>> pci_bus_has_pcie_upstream_port(bus)?
>>
>
> We should report 1 slot for a bus exposed by a PCIe Root Port
> or PCIe Downstream Port.
>
> Be aware that 1 slot doesn't necessarily refer to only one device,
> we can have a multi-function device.

Is that a fixed thing? I mean, is it always possible to use all 8 
functions of a slot (assuming no hotplug of course)?

>
> Thanks,
> Marcel
>
>> (Note that pci_bus_has_pcie_upstream_port() doesn't exist in the
>> current code, it is added by my patch)
>>
>
Markus Armbruster Dec. 14, 2016, 8:11 p.m. UTC | #11
Eduardo Habkost <ehabkost@redhat.com> writes:

> On Wed, Dec 14, 2016 at 04:34:04PM +0100, Markus Armbruster wrote:
>> Eduardo Habkost <ehabkost@redhat.com> writes:
>> 
>> > On Tue, Dec 13, 2016 at 08:51:34PM +0100, Markus Armbruster wrote:
>> >> Eduardo Habkost <ehabkost@redhat.com> writes:
>> >> 
>> >> > On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
>> >> >> Quick interface review only:
>> >> >> 
>> >> >> Eduardo Habkost <ehabkost@redhat.com> writes:
>> >> >> 
>> >> >> > This adds a new command to QMP: query-device-slots. It will allow
>> >> >> > management software to query possible slots where devices can be
>> >> >> > plugged.
>> >> >> >
>> >> >> > This implementation of the command will return:
>> >> >> >
>> >> >> > * Multiple PCI slots per bus, in the case of PCI buses;
>> >> >> > * One slot per bus in the case of the other buses;
>> >> >> 
>> >> >> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
>> >> >> slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
>> >> >> or two (PATA).  For more examples, see qdev-device-use.txt section
>> >> >> "Specifying Bus and Address on Bus".
>> >> >
>> >> > Yes, I should have clarified that: this version changes only PCI
>> >> > to expose multiple slots, but other buses also need be changed to
>> >> > implement BusClass::enumerate_slots() properly, too.
>> >> >
>> >> >> 
>> >> >> > * One slot for each entry from query-hotpluggable-cpus.
>> >> >> > In the example below, I am not sure if the PCIe ports are all
>> >> >> > supposed to report all slots, but I didn't find any existing
>> >> >> > field in PCIBus that would help me figure out the actual number
>> >> >> > of slots in a given PCI bus.
>> >> >> >
>> >> >> > Git tree
>> >> >> > --------
>> >> >> >
>> >> >> > This patch needs the previous query-machines series I am working
>> >> >> > on. The full tree can be found on the git tree at:
>> >> >> >
>> >> >> >   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
>> >> >> >
>> >> >> > Example output
>> >> >> > --------------
>> >> >> >
>> >> >> > The following output was returned by QEMU when running it as:
>> >> >> >
>> >> >> >  $ qemu-system-x86_64 -machine q35 \
>> >> >> >    -readconfig docs/q35-chipset.cfg \
>> >> >> >    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
>> >> >> >
>> >> >> >  {
>> >> >> >     "return": [
>> >> >> 
>> >> >> Are you sure >3000 lines of example output make sense here?
>> >> >
>> >> > I'm not. :)
>> >> >
>> >> > That's why I need feedback from the PCI experts. I believe most
>> >> > of the PCI ports on q35 accept only one device, but I see no code
>> >> > implementing that restriction, and no obvious PCIBus or
>> >> > PCIBusClass field indicating that.
>> >> >
>> >> >> 
>> >> >> [...]
>> >> >> >     ]
>> >> >> >   }
>> >> >> >
>> >> >> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
>> >> >> > ---
>> >> >> >  qapi-schema.json       | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
>> >> >> >  include/hw/qdev-core.h |   6 +++
>> >> >> >  hw/core/bus.c          |  49 +++++++++++++++++++++
>> >> >> >  hw/pci/pci.c           | 106 +++++++++++++++++++++++++++++++++------------
>> >> >> >  qdev-monitor.c         |  86 ++++++++++++++++++++++++++++++++++---
>> >> >> >  5 files changed, 328 insertions(+), 33 deletions(-)
>> >> >> >
>> >> >> > diff --git a/qapi-schema.json b/qapi-schema.json
>> >> >> > index d48ff3f..484d91e 100644
>> >> >> > --- a/qapi-schema.json
>> >> >> > +++ b/qapi-schema.json
>> >> >> > @@ -3166,6 +3166,120 @@
>> >> >> >  { 'command': 'closefd', 'data': {'fdname': 'str'} }
>> >> >> >  
>> >> >> >  ##
>> >> >> > +# @DeviceSlotType:
>> >> >> > +#
>> >> >> > +# Type of device slot
>> >> >> > +#
>> >> >> > +# @generic: Generic device slot, with no bus-specific information
>> >> >> > +# @pci: PCI device slot
>> >> >> > +# @cpu: CPU device slot
>> >> >> > +##
>> >> >> > +{ 'enum': 'DeviceSlotType',
>> >> >> > +  'data': ['generic', 'pci', 'cpu'] }
>> >> >> > +
>> >> >> > +##
>> >> >> > +# @DeviceSlotInfo:
>> >> >> > +#
>> >> >> > +# Information on a slot where devices can be plugged. Some buses
>> >> >> > +# are represented as a single slot that can support multiple devices,
>> >> >> > +# and some buses have multiple slots that are identified by arguments
>> >> >> > +# to @device_add.
>> >> >> 
>> >> >> Okay, let me try to wrap my poor, ignorant mind around this.
>> >> >> 
>> >> >> There are two kinds of buses: "single slot that can support multiple
>> >> >> devices", and "multiple slots that are identified by arguments".
>> >> >> 
>> >> >> How are the two kinds related to @type?
>> >> >
>> >> > They are related to @type indirectly because different bus types
>> >> > will return different data. But I don't want to make this part of
>> >> > the specification: clients should be prepared to handle both
>> >> > cases.
>> >> 
>> >> Well, color me confused :)
>> >> 
>> >> > e.g. a QEMU version might return a single generic catch-all
>> >> > sysbus-device slot that support any number of devices. Future
>> >> > versions may return more detailed information, and return slots
>> >> > only for the sysbus devices that really work with the machine.
>> >> 
>> >> Hmm.  See below.
>> >> 
>> >> >> 
>> >> >> Examples for "multiple slots that are identified by arguments":
>> >> >> 
>> >> >>     -device edu,addr=06.0,bus=...
>> >> >>     -device scsi-hd,scsi-hd=5,bus=...
>> >> >>     -device ide-hd,unit=1,bus=...
>> >> >>     -device virtserialport,nr=7,bus=...
>> >> >> 
>> >> >> Note that each of these buses has its own way to specify a slot address,
>> >> >> namely a bus-specific option.
>> >> >
>> >> > Yes.
>> >> >
>> >> >> 
>> >> >> Can you give examples for "single slot that can support multiple
>> >> >> devices"?
>> >> >
>> >> > I can't name any example except sysbus, right now.
>> >> 
>> >> Sysbus is a bad example, because it's a hack, not a bus.
>> >> 
>> >> Physical devices are wired up in some device-specific way.  An important
>> >> special wiring case is plugging into a standard socket provided by a
>> >> bus.  But not every device is connected only to a bus.  Devices can also
>> >> be wired to other devices in ad hoc ways.
>> >> 
>> >> In the initial qdev design, devices could only plug into buses.
>> >> Anything that didn't fit the mold was declared to plug into the "system
>> >> bus", which isn't a bus at all.
>> >> 
>> >> The hack used to be fairly harmless, because you couldn't do much with
>> >> system bus devices anyway.  Not true anymore: Alex created means to add
>> >> certain system bus devices to certain machines with -device.  Has been a
>> >> thorn in my side ever since.  I'm afraid we need to understand how
>> >> exactly it works before we can finalize the design for this command.
>> >
>> > I agree it is a hack, I just wanted to be able to cover it too
>> > (and possibly other cases where there's no obvious "address"
>> > parameter to devices).
>> >
>> > The other options I had were:
>> > a) Omitting the bus from the output;
>> > b) Waiting until we fix sysbus before implementing the interface.
>> >
>> > I avoided (a) because I would like to tell libvirt "always check
>> > query-device-slots before plugging anything. if it's not there,
>> > it means you can't plug it". To do that, I would need to ensure
>> > all buses are present in the list (even sysbus).
>> >
>> > (But as I say below: I don't love this solution and I am open to
>> > suggestions).
>> 
>> I'm okay with doing (c) including all buses, with full information for
>> only some.  As long as the case "no full information" is clearly
>> separate, and appropriately documented.  Then we can tell libvirt to
>> always check query-device-slots, and if it has full information, use
>> that.  Else, falling back to hardcoded strategies is okay, similar to
>> what's done when command query-device-slots doesn't exist.
>
> Understood.
>
>> 
>> >> > There are two cases that I tried to cover by reporting
>> >> > multiple-device no-argument slots:
>> >> >
>> >> > 1) Buses that were not converted (yet) to implement
>> >> >    enumerate_buses() (in the current version: everything except
>> >> >    PCI)
>> >> > 2) Buses that really do not require any extra slot address
>> >> >    argument (sysbus? others?)
>> >> >
>> >> > I'm proposing this interface because I don't want (1) or (2) to
>> >> > be missing from the returned data (otherwise management software
>> >> > can't differentiate "this bus is not available" from "this bus
>> >> > simply doesn't implement slot enumeration yet").
>> >> >
>> >> > We won't need the special multi-device-slot case if we eliminate
>> >> > all instances of (1) and (2). Can we do that in 2.9? I'm not
>> >> > sure.
>> >> 
>> >> If we can't do a complete job, and we don't want to hide slots affected
>> >> by that, the data we return for them should unequivocally state that
>> >> it's incomplete because computing complete data hasn't been implemented,
>> >> yet.
>> >
>> > OK, so we agree we need a way to tell the client the data is
>> > incomplete (or not as detailed as we would like).
>> 
>> Yes.
>> 
>> > I tried to solve that by providing a mechanism to tell the client
>> > "look, I can tell you that there's a bus called <bus name>, and
>> > it is valid to use it as the 'bus' argument for -device and
>> > device_add, but I don't know the rest of the rules (sorry!)". We
>> > can do that by simply including the bus on the list as if it was
>> > a multi-device slot.
>> 
>> What if we ever run into a kind of slot where the full information is
>> "use this 'bus' argument" and "you can plug in multiple devices"?
>> Wouldn't that be indistinguishable from "full information isn't
>> available"?
>> 
>> Let's make "full information isn't available" impossible to confuse with
>> any conceivable set of full information.
>
> I see. I tried to model it in a way that "incomplete information"
> was not considered an exception, just an instance where the data
> is less informative. But adding another bit of information that
> explicitly says "this in incomplete and will probably change in
> the future" is a good idea.
>
>> 
>> > But I don't love this solution, so I am open to other suggestions
>> > on how to tell the client that slot information is incomplete for
>> > a bus.
>> >
>> > Maybe the following?
>> >
>> > 1) Add a new DeviceSlotType value meaning "this entry represents
>> >    a bus/device-type that can't enumerate its slots" (let's call
>> >    it "bus-with-no-slot-info" by now).
>> 
>> Let's call this one a non-slot, for brevity.
>
> I like that name. :)
>
>> 
>> > 2) Remove DeviceSlotType "generic".
>> > 3) Remove max-devices field, and simply document the device-limit
>> >    semantics for each DeviceSlotType (cpu: 1 device, pci: 1
>> >    device, bus-with-no-slot-info: undefined limit).
>> 
>> Not sure there's much to document.  For me a slot takes exactly one plug
>> by definition (if it can take more, it's a set of slots, isn't it?), and
>> non-slots take an unknown number of plugs by definition (if we knew,
>> we'd return suitable slots instead).  But that's mincing words; I'm sure
>> we can come up with language that works for both of us.
>
> I am trying to not make any guarantees about all slot types,
> until we learn more about the more complex cases. All we can say
> right now is that CPU and PCI slots support only 1 device, and we
> don't claim anything about others.

Sane physical buses generally have a "device address" concept: a device
on the bus is identified by its (unique) device address.  For instance,
SCSI has scsi-id, and PCI has device number.

Less-than-sane buses exist, such as the (pre-PnP) ISA "bus": there's no
device address that satisfies my definition.

> I'm not even 100% sure about the 1-device rule for PCI, as each
> PCI function in a slot is a separate DeviceState*. I need to
> understand how multi-function PCI hotplug works, to find out what
> we should do.

*Real* PCI devices consist of function 0 and optionally additional
functions.  The device gets plugged as a whole (d'oh) into a slot with a
specific device address.  Note that the dev.fn address addresses a
*function*, not a device.

The way QEMU models this is "madness, yet there is method in't".  A PCI
qdev models a PCI *function*, not a PCI device.  It gets plugged into a
PCI qbus "slot" with a specific *function* address (dev.fn).  The
functions that share the dev part of the function address together form
a PCI device.  You have to plug something into function 0 for this to
work.  Sufficiently weird function combinations might conceivably
confuse software.  If I remember correctly, you have to hot-plug
function 0 last (actually triggers the device hot-plug), and
hot-unplugging function 0 yanks out all the functions.

When a device has just one function, which is a fairly common case, then
you can pretend the PCI qdev is a PCI device.

So, what are the slots provided by a PCI (not PCIe) bus?  A *real* one
provides 32 *device* slots.  A qbus, however, provides 256 *function*
"slots".

How should query-device-slots report this?  All 256 function "slots", or
just the 32 function 0 slots?  Since management software needs to know
about the PCI qdev madness anyway, I'd say just the 32.

Same question for PCIe, with and without ARI.

If we had had QOM back when we made this mess, we'd have designed PCI
devices as a composition of PCI functions.  At least I hope we'd have.
With such a design, we'd assemble functions into a device, then plug the
device.  A slot would then be the same as for a real bus.

> I worry about having a general 1-slot = 1-device rule from the
> beginning, because I don't know if it can be a problem when the
> set of possible slots is too large. e.g.: should we return one
> slot for each of the 256 possible devfns in a PCI bus? What if a
> bus uses 32-bit identifiers for devices?
>
> My suggestion is to avoid any general claims about device limits
> in slots in the documentation by now, until we have more
> knowledge about other bus types.

For me, a slot can't take more than one device.  Something that can
isn't a slot, it's a set of slots.

Comitting to a design now that models this as a "slot" that can take
multiple devices seems premature.  We really have no idea whether that
would work!

But you made a good point: set of slots too large to permit
query-device-slots enumerating its members may exist.

Enumerating members is just one way to describe a set.  An algorithm to
generate the members is another.

Let's try for your hypothetical bus with 2^32 slots, each identified by
an unsigned 32-bit number.  Say the syntax is bus=BUSNAME,addr=UINT32.
We need to describe bus=BUSNAME,addr=UINT32.  The bus=BUSNAME part is
simple enough: "props" contains a member "bus": "BUSNAME".  For the
addr=UINT32 part, we'll have to invent suitable language to express "any
integer between 0 and 2^32-1".

I believe this will be easier when the information for all slots is
otherwise identical.  If it is, we can describe all 2^32 slots in one
dictionary.  But if slot#7 and slot#42..666 are different, we need
multiple, and probably a more complex language for slot addresses, to
express things like "any integer between 0 and 2^32-1, except for 7 and
42..666".

Note that including information on currently plugged devices makes slot
information different.

Let's take a step back and consider what we want to achieve with
query-device-slots.  I think the core objective is to let libvirt know:

* What slots exist

* What devices can be plugged into each slot

* Whether plugging / unplugging is possible right now

* What arguments need to be used to plug something into a specific slot

Your RFC design provides additional information, namely

* Currently plugged devices

  I'd expect this to be redundant with other queries, such as QOM
  introspection.

* Whether hot-plug is possible

  If the machine has been started already, this is the same as whether
  plugging / unplugging is possible right now.

  Else, it permits predicting whether it will be possible once the
  machine is started.  Perhaps that's important to know, perhaps not.

* Perhaps (I'm not sure) even internal-only slots, i.e. ones that can
  never be used with device_add.

Let's focus on the core objective and completely ignore the additional
information for now.

>> > This looks like a trade-off between moving data to specific union
>> > types (and representing type-specific limits/rules in the schema
>> > documentation), or moving data to the base union type (and
>> > representing type-specific limits as data in the base union
>> > type).
>> 
>> Works for me.
>> 
>> Additionally, let's replace 'devices': [ 'str' ] by '*device': 'str',
>> because a slot is either empty or has one device plugged in.
>> 
>> If we ever need to add a set-of-slots type, we'll have to extend the
>> schema, but then we'll be in a much better position to know what we
>> actually need.
>> 
>
> I am working on a version that even removes 'devices', for
> simplicity. Then we would have more freedom when extending the
> schema later.

Yes, please.

>> >> >> > +#
>> >> >> > +# @bus: ID of the bus object where the device can be plugged. Optional,
>> >> >> > +#       as some devices don't need a bus to be plugged (e.g. CPUs).
>> >> >> > +#       Can be copied to the "bus" argument to @device_add.
>> >> >> 
>> >> >> Suggest something like "Can be used as value for @device_add's bus
>> >> >> option".
>> >> >
>> >> > Will do.
>> >> >
>> >> >> 
>> >> >> Should we give similar information on the slot address?  The option name
>> >> >> is obvious.  What about acceptable values?
>> >> >
>> >> > Actually, this part of the documentation was a leftover from a
>> >> > previous version where @bus was inside DeviceSlotInfo. Now I
>> >> > moved @bus inside @props for all device types, and it should be
>> >> > documented the same way as the other fields inside @props.
>> >> >
>> >> >> 
>> >> >> > +#
>> >> >> > +# @type: type of device slot.
>> >> >> > +#
>> >> >> > +# @accepted-device-types: List of device types accepted by the slot.
>> >> >> > +#                         Any device plugged to the slot should implement
>> >> >> > +#                         one of the accepted device types.
>> >> >> > +#
>> >> >> > +# @max-devices: #optional maximum number of devices that can be plugged
>> >> >> > +#               to the slot.
>> >> >> 
>> >> >> What does it mean when @max-devices isn't given?
>> >> >
>> >> > It means there is no hard limit on the number of pluggable
>> >> > devices. sysbus is one of such cases.
>> >> 
>> >> Sysbus certainly has limits, but they're more complicated than just "at
>> >> most @max-devices devices".
>> >> 
>> >> >> > +#
>> >> >> > +# @devices: List of QOM paths for devices that are already plugged.
>> >> >> > +#
>> >> >> > +# @available: If false, the slot is not available for plugging any device.
>> >> >> > +#             This value can change at runtime if condition changes
>> >> >> > +#             (e.g. if the device becomes full, or if the machine
>> >> >> > +#             was already initialized and the slot doesn't support
>> >> >> > +#             hotplug).
>> >> >> > +#
>> >> >> > +# @hotpluggable: If true, the slot accepts hotplugged devices.
>> >> >> > +#
>> >> >> > +# DeviceSlotInfo structs always have a @props member, whose members
>> >> >> > +# can be directly copied to the arguments to @device_add.
>> >> >> 
>> >> >> Do you mean names of properties common to all @accepted-device-types?
>> >> >
>> >> > I mean that it should be safe to simply use the keys+values in
>> >> > @props as arguments to device_add or -device, for any slot @type.
>> >> > Some clients may want to extract some information from @props (if
>> >> > they already manage PCI addresses, for example), but some of them
>> >> > may simply choose any available slot and copy @props blindly.
>> >> >
>> >> > I didn't want to state anything about QOM properties, because I
>> >> > only want @props to represent device_add/-device arguments. Some
>> >> > of those arguments are consumed by the device_add code itself
>> >> > (e.g. "bus") and others are translated to QOM properties. I don't
>> >> > want the interface to impose any restrictions on how this is
>> >> > implemented.
>> >> 
>> >> Hmm, is this meant to work as follows?  To plug a device into this slot,
>> >> you use
>> >> 
>> >>     { "execute": "device_add",
>> >>       "arguments": { "driver": TYPE, ... PROPS ... }
>> >> 
>> >> where TYPE is a (concrete subtype of a) member of
>> >> @accepted-device-types, and PROPS is the value of @props spliced in.
>> >> Example: the data for slot 06.0 of PCI bus pci.0 would be something like
>> >> 
>> >>     {
>> >>       "bus": "pci.0",
>> >
>> > There's no "bus" field in DeviceSlotInfo. It was a documentation bug.
>> 
>> Should read code, not docs ;)
>> 
>> >>       "props": {
>> >>           "bus": "pci.0",
>> >>           "addr": "06.0"
>> >>       }
>> >>     }
>> >> 
>> >> Doesn't match your example output; I guess I'm still misunderstanding
>> >> something.
>> >
>> > This is a busy PCI slot, from my example:
>> >
>> >         {
>> >             "available": false,
>> >             "devices": [
>> >                 "/machine/q35/mch"
>> >             ],
>> >             "accepted-device-types": [
>> >                 "legacy-pci-device",
>> >                 "pci-express-device"
>> >             ],
>> >             "props": {
>> >                 "bus": "/machine/q35/pcie.0",
>> 
>> Does device_add device_add bus=/machine/q35/pcie.0 even work?  I know
>> qbus_find() interprets /-separated paths, but their semantics are FUBAR
>> (I can find details if you're curious).  We've long advised to use only
>> values without '/', like bus=pcie.0.
>
> @device_add documentation says:
>
> # @bus: #optional the device's parent bus (device tree path)

The line is a recent addition (commit 94cfd07).  Note "device tree
path", not "QOM path".  I think it should either not mention paths at
all, or advise people to stick to qdev IDs.

>> Now, QOM gives us a framework for interpreting paths sanely.  Switching
>> the value of bus to QOM paths would be a compatibility break, though.
>> Matters only if the botched /-paths are actually used.
>
> I would be happy to use the bus name instead of the full path,
> but I don't see any code that ensures that bus names are really
> unique. Is there any existing mechanism to ensure that?

Kind of.  qbus names are typically derived from the providing device's
qdev ID, which is unique.  When they're not, clashes *are* possible.  We
used to have one with -m isapc: the two IDE buses are provided by
separate isa-ide qdevs, and both qbuses were named ide.0.  Fixed in
commit 61de36.  Now the first one to register becomes ide.0, and the
second one ide.1.  I'm not fond of such implicit naming, but we got
bigger fish to fry.

Perhaps we should sidestep this mess and use QOM paths.

>> >                 "addr": 0
>> >             },
>> >             "hotpluggable": false,
>> >             "type": "pci",
>> >             "max-devices": 1
>> >         },
[...]
Marcel Apfelbaum Dec. 15, 2016, 10:12 a.m. UTC | #12
On 12/14/2016 10:10 PM, Laine Stump wrote:
> On 12/14/2016 12:17 PM, Marcel Apfelbaum wrote:
>> On 12/13/2016 06:00 PM, Eduardo Habkost wrote:
>>> On Tue, Dec 13, 2016 at 04:15:18PM +0200, Marcel Apfelbaum wrote:
>>>> On 12/13/2016 02:42 PM, Eduardo Habkost wrote:
>>>>> On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
>>>>>> Quick interface review only:
>>>>>>
>>>>>> Eduardo Habkost <ehabkost@redhat.com> writes:
>>>>>>
>>>>>>> This adds a new command to QMP: query-device-slots. It will allow
>>>>>>> management software to query possible slots where devices can be
>>>>>>> plugged.
>>>>>>>
>>>>>>> This implementation of the command will return:
>>>>>>>
>>>>>>> * Multiple PCI slots per bus, in the case of PCI buses;
>>>>>>> * One slot per bus in the case of the other buses;
>>>>>>
>>>>>> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
>>>>>> slots.  The slot address is the SCSI ID.  An IDE bus may have one
>>>>>> (SATA)
>>>>>> or two (PATA).  For more examples, see qdev-device-use.txt section
>>>>>> "Specifying Bus and Address on Bus".
>>>>>
>>>>> Yes, I should have clarified that: this version changes only PCI
>>>>> to expose multiple slots, but other buses also need be changed to
>>>>> implement BusClass::enumerate_slots() properly, too.
>>>>>
>>>>>>
>>>>>>> * One slot for each entry from query-hotpluggable-cpus.
>>>>>>> In the example below, I am not sure if the PCIe ports are all
>>>>>>> supposed to report all slots, but I didn't find any existing
>>>>>>> field in PCIBus that would help me figure out the actual number
>>>>>>> of slots in a given PCI bus.
>>>>>>>
>>>>>>> Git tree
>>>>>>> --------
>>>>>>>
>>>>>>> This patch needs the previous query-machines series I am working
>>>>>>> on. The full tree can be found on the git tree at:
>>>>>>>
>>>>>>>   git://github.com/ehabkost/qemu-hacks.git
>>>>>>> work/query-machines-bus-info
>>>>>>>
>>>>>>> Example output
>>>>>>> --------------
>>>>>>>
>>>>>>> The following output was returned by QEMU when running it as:
>>>>>>>
>>>>>>>  $ qemu-system-x86_64 -machine q35 \
>>>>>>>    -readconfig docs/q35-chipset.cfg \
>>>>>>>    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
>>>>>>>
>>>>>>>  {
>>>>>>>     "return": [
>>>>>>
>>>>>> Are you sure >3000 lines of example output make sense here?
>>>>>
>>>>> I'm not. :)
>>>>>
>>>>> That's why I need feedback from the PCI experts. I believe most
>>>>> of the PCI ports on q35 accept only one device,
>
> I don't think of it as "one device" but as "only slot 0 can be used" (since, as Marcel pointed out, a different device can be placed on each of the 8 functions of that slot).
>
>> but I see no code
>>>>> implementing that restriction, and no obvious PCIBus or
>>>>> PCIBusClass field indicating that.
>>>>
>>>> Hi,
>>>>
>>>> Sadly there is no such restriction today as far as I know.
>>>> We trust the user that he knows what he is doing :)
>>>> and we let libvirt implement the restriction.
>>>>
>>>> I do agree we should fix that.
>>>
>>> OK, this matches what I saw on the current code.
>>>
>>> But I would like to understand what exactly should be the rules
>>> if we fix that. Should we report only 1 slot if
>>> pci_bus_has_pcie_upstream_port(bus)?
>>>
>>
>> We should report 1 slot for a bus exposed by a PCIe Root Port
>> or PCIe Downstream Port.
>>
>> Be aware that 1 slot doesn't necessarily refer to only one device,
>> we can have a multi-function device.
>

Hi Laine,

> Is that a fixed thing? I mean, is it always possible to use all 8 functions of a slot (assuming no hotplug of course)?
>

Yes, you simply add to the function 0 on command line:  with -device ...,multifunction=on.

Thanks,
Marcel


>>
>> Thanks,
>> Marcel
>>
>>> (Note that pci_bus_has_pcie_upstream_port() doesn't exist in the
>>> current code, it is added by my patch)
>>>
>>
>
Eduardo Habkost Dec. 15, 2016, 7:04 p.m. UTC | #13
On Wed, Dec 14, 2016 at 09:11:10PM +0100, Markus Armbruster wrote:
> Eduardo Habkost <ehabkost@redhat.com> writes:
> 
> > On Wed, Dec 14, 2016 at 04:34:04PM +0100, Markus Armbruster wrote:
> >> Eduardo Habkost <ehabkost@redhat.com> writes:
> >> 
> >> > On Tue, Dec 13, 2016 at 08:51:34PM +0100, Markus Armbruster wrote:
> >> >> Eduardo Habkost <ehabkost@redhat.com> writes:
> >> >> 
> >> >> > On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
> >> >> >> Quick interface review only:
> >> >> >> 
> >> >> >> Eduardo Habkost <ehabkost@redhat.com> writes:
> >> >> >> 
> >> >> >> > This adds a new command to QMP: query-device-slots. It will allow
> >> >> >> > management software to query possible slots where devices can be
> >> >> >> > plugged.
> >> >> >> >
> >> >> >> > This implementation of the command will return:
> >> >> >> >
> >> >> >> > * Multiple PCI slots per bus, in the case of PCI buses;
> >> >> >> > * One slot per bus in the case of the other buses;
> >> >> >> 
> >> >> >> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
> >> >> >> slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
> >> >> >> or two (PATA).  For more examples, see qdev-device-use.txt section
> >> >> >> "Specifying Bus and Address on Bus".
> >> >> >
> >> >> > Yes, I should have clarified that: this version changes only PCI
> >> >> > to expose multiple slots, but other buses also need be changed to
> >> >> > implement BusClass::enumerate_slots() properly, too.
> >> >> >
> >> >> >> 
> >> >> >> > * One slot for each entry from query-hotpluggable-cpus.
> >> >> >> > In the example below, I am not sure if the PCIe ports are all
> >> >> >> > supposed to report all slots, but I didn't find any existing
> >> >> >> > field in PCIBus that would help me figure out the actual number
> >> >> >> > of slots in a given PCI bus.
> >> >> >> >
> >> >> >> > Git tree
> >> >> >> > --------
> >> >> >> >
> >> >> >> > This patch needs the previous query-machines series I am working
> >> >> >> > on. The full tree can be found on the git tree at:
> >> >> >> >
> >> >> >> >   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
> >> >> >> >
> >> >> >> > Example output
> >> >> >> > --------------
> >> >> >> >
> >> >> >> > The following output was returned by QEMU when running it as:
> >> >> >> >
> >> >> >> >  $ qemu-system-x86_64 -machine q35 \
> >> >> >> >    -readconfig docs/q35-chipset.cfg \
> >> >> >> >    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
> >> >> >> >
> >> >> >> >  {
> >> >> >> >     "return": [
> >> >> >> 
> >> >> >> Are you sure >3000 lines of example output make sense here?
> >> >> >
> >> >> > I'm not. :)
> >> >> >
> >> >> > That's why I need feedback from the PCI experts. I believe most
> >> >> > of the PCI ports on q35 accept only one device, but I see no code
> >> >> > implementing that restriction, and no obvious PCIBus or
> >> >> > PCIBusClass field indicating that.
> >> >> >
> >> >> >> 
> >> >> >> [...]
> >> >> >> >     ]
> >> >> >> >   }
> >> >> >> >
> >> >> >> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> >> >> >> > ---
> >> >> >> >  qapi-schema.json       | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
> >> >> >> >  include/hw/qdev-core.h |   6 +++
> >> >> >> >  hw/core/bus.c          |  49 +++++++++++++++++++++
> >> >> >> >  hw/pci/pci.c           | 106 +++++++++++++++++++++++++++++++++------------
> >> >> >> >  qdev-monitor.c         |  86 ++++++++++++++++++++++++++++++++++---
> >> >> >> >  5 files changed, 328 insertions(+), 33 deletions(-)
> >> >> >> >
> >> >> >> > diff --git a/qapi-schema.json b/qapi-schema.json
> >> >> >> > index d48ff3f..484d91e 100644
> >> >> >> > --- a/qapi-schema.json
> >> >> >> > +++ b/qapi-schema.json
> >> >> >> > @@ -3166,6 +3166,120 @@
> >> >> >> >  { 'command': 'closefd', 'data': {'fdname': 'str'} }
> >> >> >> >  
> >> >> >> >  ##
> >> >> >> > +# @DeviceSlotType:
> >> >> >> > +#
> >> >> >> > +# Type of device slot
> >> >> >> > +#
> >> >> >> > +# @generic: Generic device slot, with no bus-specific information
> >> >> >> > +# @pci: PCI device slot
> >> >> >> > +# @cpu: CPU device slot
> >> >> >> > +##
> >> >> >> > +{ 'enum': 'DeviceSlotType',
> >> >> >> > +  'data': ['generic', 'pci', 'cpu'] }
> >> >> >> > +
> >> >> >> > +##
> >> >> >> > +# @DeviceSlotInfo:
> >> >> >> > +#
> >> >> >> > +# Information on a slot where devices can be plugged. Some buses
> >> >> >> > +# are represented as a single slot that can support multiple devices,
> >> >> >> > +# and some buses have multiple slots that are identified by arguments
> >> >> >> > +# to @device_add.
> >> >> >> 
> >> >> >> Okay, let me try to wrap my poor, ignorant mind around this.
> >> >> >> 
> >> >> >> There are two kinds of buses: "single slot that can support multiple
> >> >> >> devices", and "multiple slots that are identified by arguments".
> >> >> >> 
> >> >> >> How are the two kinds related to @type?
> >> >> >
> >> >> > They are related to @type indirectly because different bus types
> >> >> > will return different data. But I don't want to make this part of
> >> >> > the specification: clients should be prepared to handle both
> >> >> > cases.
> >> >> 
> >> >> Well, color me confused :)
> >> >> 
> >> >> > e.g. a QEMU version might return a single generic catch-all
> >> >> > sysbus-device slot that support any number of devices. Future
> >> >> > versions may return more detailed information, and return slots
> >> >> > only for the sysbus devices that really work with the machine.
> >> >> 
> >> >> Hmm.  See below.
> >> >> 
> >> >> >> 
> >> >> >> Examples for "multiple slots that are identified by arguments":
> >> >> >> 
> >> >> >>     -device edu,addr=06.0,bus=...
> >> >> >>     -device scsi-hd,scsi-hd=5,bus=...
> >> >> >>     -device ide-hd,unit=1,bus=...
> >> >> >>     -device virtserialport,nr=7,bus=...
> >> >> >> 
> >> >> >> Note that each of these buses has its own way to specify a slot address,
> >> >> >> namely a bus-specific option.
> >> >> >
> >> >> > Yes.
> >> >> >
> >> >> >> 
> >> >> >> Can you give examples for "single slot that can support multiple
> >> >> >> devices"?
> >> >> >
> >> >> > I can't name any example except sysbus, right now.
> >> >> 
> >> >> Sysbus is a bad example, because it's a hack, not a bus.
> >> >> 
> >> >> Physical devices are wired up in some device-specific way.  An important
> >> >> special wiring case is plugging into a standard socket provided by a
> >> >> bus.  But not every device is connected only to a bus.  Devices can also
> >> >> be wired to other devices in ad hoc ways.
> >> >> 
> >> >> In the initial qdev design, devices could only plug into buses.
> >> >> Anything that didn't fit the mold was declared to plug into the "system
> >> >> bus", which isn't a bus at all.
> >> >> 
> >> >> The hack used to be fairly harmless, because you couldn't do much with
> >> >> system bus devices anyway.  Not true anymore: Alex created means to add
> >> >> certain system bus devices to certain machines with -device.  Has been a
> >> >> thorn in my side ever since.  I'm afraid we need to understand how
> >> >> exactly it works before we can finalize the design for this command.
> >> >
> >> > I agree it is a hack, I just wanted to be able to cover it too
> >> > (and possibly other cases where there's no obvious "address"
> >> > parameter to devices).
> >> >
> >> > The other options I had were:
> >> > a) Omitting the bus from the output;
> >> > b) Waiting until we fix sysbus before implementing the interface.
> >> >
> >> > I avoided (a) because I would like to tell libvirt "always check
> >> > query-device-slots before plugging anything. if it's not there,
> >> > it means you can't plug it". To do that, I would need to ensure
> >> > all buses are present in the list (even sysbus).
> >> >
> >> > (But as I say below: I don't love this solution and I am open to
> >> > suggestions).
> >> 
> >> I'm okay with doing (c) including all buses, with full information for
> >> only some.  As long as the case "no full information" is clearly
> >> separate, and appropriately documented.  Then we can tell libvirt to
> >> always check query-device-slots, and if it has full information, use
> >> that.  Else, falling back to hardcoded strategies is okay, similar to
> >> what's done when command query-device-slots doesn't exist.
> >
> > Understood.
> >
> >> 
> >> >> > There are two cases that I tried to cover by reporting
> >> >> > multiple-device no-argument slots:
> >> >> >
> >> >> > 1) Buses that were not converted (yet) to implement
> >> >> >    enumerate_buses() (in the current version: everything except
> >> >> >    PCI)
> >> >> > 2) Buses that really do not require any extra slot address
> >> >> >    argument (sysbus? others?)
> >> >> >
> >> >> > I'm proposing this interface because I don't want (1) or (2) to
> >> >> > be missing from the returned data (otherwise management software
> >> >> > can't differentiate "this bus is not available" from "this bus
> >> >> > simply doesn't implement slot enumeration yet").
> >> >> >
> >> >> > We won't need the special multi-device-slot case if we eliminate
> >> >> > all instances of (1) and (2). Can we do that in 2.9? I'm not
> >> >> > sure.
> >> >> 
> >> >> If we can't do a complete job, and we don't want to hide slots affected
> >> >> by that, the data we return for them should unequivocally state that
> >> >> it's incomplete because computing complete data hasn't been implemented,
> >> >> yet.
> >> >
> >> > OK, so we agree we need a way to tell the client the data is
> >> > incomplete (or not as detailed as we would like).
> >> 
> >> Yes.
> >> 
> >> > I tried to solve that by providing a mechanism to tell the client
> >> > "look, I can tell you that there's a bus called <bus name>, and
> >> > it is valid to use it as the 'bus' argument for -device and
> >> > device_add, but I don't know the rest of the rules (sorry!)". We
> >> > can do that by simply including the bus on the list as if it was
> >> > a multi-device slot.
> >> 
> >> What if we ever run into a kind of slot where the full information is
> >> "use this 'bus' argument" and "you can plug in multiple devices"?
> >> Wouldn't that be indistinguishable from "full information isn't
> >> available"?
> >> 
> >> Let's make "full information isn't available" impossible to confuse with
> >> any conceivable set of full information.
> >
> > I see. I tried to model it in a way that "incomplete information"
> > was not considered an exception, just an instance where the data
> > is less informative. But adding another bit of information that
> > explicitly says "this in incomplete and will probably change in
> > the future" is a good idea.
> >
> >> 
> >> > But I don't love this solution, so I am open to other suggestions
> >> > on how to tell the client that slot information is incomplete for
> >> > a bus.
> >> >
> >> > Maybe the following?
> >> >
> >> > 1) Add a new DeviceSlotType value meaning "this entry represents
> >> >    a bus/device-type that can't enumerate its slots" (let's call
> >> >    it "bus-with-no-slot-info" by now).
> >> 
> >> Let's call this one a non-slot, for brevity.
> >
> > I like that name. :)
> >
> >> 
> >> > 2) Remove DeviceSlotType "generic".
> >> > 3) Remove max-devices field, and simply document the device-limit
> >> >    semantics for each DeviceSlotType (cpu: 1 device, pci: 1
> >> >    device, bus-with-no-slot-info: undefined limit).
> >> 
> >> Not sure there's much to document.  For me a slot takes exactly one plug
> >> by definition (if it can take more, it's a set of slots, isn't it?), and
> >> non-slots take an unknown number of plugs by definition (if we knew,
> >> we'd return suitable slots instead).  But that's mincing words; I'm sure
> >> we can come up with language that works for both of us.
> >
> > I am trying to not make any guarantees about all slot types,
> > until we learn more about the more complex cases. All we can say
> > right now is that CPU and PCI slots support only 1 device, and we
> > don't claim anything about others.
> 
> Sane physical buses generally have a "device address" concept: a device
> on the bus is identified by its (unique) device address.  For instance,
> SCSI has scsi-id, and PCI has device number.
> 
> Less-than-sane buses exist, such as the (pre-PnP) ISA "bus": there's no
> device address that satisfies my definition.
> 
> > I'm not even 100% sure about the 1-device rule for PCI, as each
> > PCI function in a slot is a separate DeviceState*. I need to
> > understand how multi-function PCI hotplug works, to find out what
> > we should do.
> 
> *Real* PCI devices consist of function 0 and optionally additional
> functions.  The device gets plugged as a whole (d'oh) into a slot with a
> specific device address.  Note that the dev.fn address addresses a
> *function*, not a device.
> 
> The way QEMU models this is "madness, yet there is method in't".  A PCI
> qdev models a PCI *function*, not a PCI device.  It gets plugged into a
> PCI qbus "slot" with a specific *function* address (dev.fn).  The
> functions that share the dev part of the function address together form
> a PCI device.  You have to plug something into function 0 for this to
> work.  Sufficiently weird function combinations might conceivably
> confuse software.  If I remember correctly, you have to hot-plug
> function 0 last (actually triggers the device hot-plug), and
> hot-unplugging function 0 yanks out all the functions.
> 
> When a device has just one function, which is a fairly common case, then
> you can pretend the PCI qdev is a PCI device.
> 
> So, what are the slots provided by a PCI (not PCIe) bus?  A *real* one
> provides 32 *device* slots.  A qbus, however, provides 256 *function*
> "slots".
> 
> How should query-device-slots report this?  All 256 function "slots", or
> just the 32 function 0 slots?  Since management software needs to know
> about the PCI qdev madness anyway, I'd say just the 32.

So, if weach PCI function is a separate device for QEMU, isn't
this breaking your suggested rule about each slot supporting only
1 device?

> 
> Same question for PCIe, with and without ARI.
> 
> If we had had QOM back when we made this mess, we'd have designed PCI
> devices as a composition of PCI functions.  At least I hope we'd have.
> With such a design, we'd assemble functions into a device, then plug the
> device.  A slot would then be the same as for a real bus.
> 
> > I worry about having a general 1-slot = 1-device rule from the
> > beginning, because I don't know if it can be a problem when the
> > set of possible slots is too large. e.g.: should we return one
> > slot for each of the 256 possible devfns in a PCI bus? What if a
> > bus uses 32-bit identifiers for devices?
> >
> > My suggestion is to avoid any general claims about device limits
> > in slots in the documentation by now, until we have more
> > knowledge about other bus types.
> 
> For me, a slot can't take more than one device.  Something that can
> isn't a slot, it's a set of slots.

So, is a PCI device number (that supports up to 8 functions, each
with its own device object) a slot, or a set of slots?

> 
> Comitting to a design now that models this as a "slot" that can take
> multiple devices seems premature.  We really have no idea whether that
> would work!
> 
> But you made a good point: set of slots too large to permit
> query-device-slots enumerating its members may exist.
> 
> Enumerating members is just one way to describe a set.  An algorithm to
> generate the members is another.
> 
> Let's try for your hypothetical bus with 2^32 slots, each identified by
> an unsigned 32-bit number.  Say the syntax is bus=BUSNAME,addr=UINT32.
> We need to describe bus=BUSNAME,addr=UINT32.  The bus=BUSNAME part is
> simple enough: "props" contains a member "bus": "BUSNAME".  For the
> addr=UINT32 part, we'll have to invent suitable language to express "any
> integer between 0 and 2^32-1".
> 
> I believe this will be easier when the information for all slots is
> otherwise identical.  If it is, we can describe all 2^32 slots in one
> dictionary.  But if slot#7 and slot#42..666 are different, we need
> multiple, and probably a more complex language for slot addresses, to
> express things like "any integer between 0 and 2^32-1, except for 7 and
> 42..666".

The question for me is: do we need it now, or can we add this as
an extension in a future version?

> 
> Note that including information on currently plugged devices makes slot
> information different.
> 
> Let's take a step back and consider what we want to achieve with
> query-device-slots.  I think the core objective is to let libvirt know:
> 
> * What slots exist
> 
> * What devices can be plugged into each slot
> 
> * Whether plugging / unplugging is possible right now
> 
> * What arguments need to be used to plug something into a specific slot
> 
> Your RFC design provides additional information, namely
> 
> * Currently plugged devices
> 
>   I'd expect this to be redundant with other queries, such as QOM
>   introspection.
> 
> * Whether hot-plug is possible
> 
>   If the machine has been started already, this is the same as whether
>   plugging / unplugging is possible right now.
> 
>   Else, it permits predicting whether it will be possible once the
>   machine is started.  Perhaps that's important to know, perhaps not.
> 
> * Perhaps (I'm not sure) even internal-only slots, i.e. ones that can
>   never be used with device_add.
> 
> Let's focus on the core objective and completely ignore the additional
> information for now.
> 
> >> > This looks like a trade-off between moving data to specific union
> >> > types (and representing type-specific limits/rules in the schema
> >> > documentation), or moving data to the base union type (and
> >> > representing type-specific limits as data in the base union
> >> > type).
> >> 
> >> Works for me.
> >> 
> >> Additionally, let's replace 'devices': [ 'str' ] by '*device': 'str',
> >> because a slot is either empty or has one device plugged in.
> >> 
> >> If we ever need to add a set-of-slots type, we'll have to extend the
> >> schema, but then we'll be in a much better position to know what we
> >> actually need.
> >> 
> >
> > I am working on a version that even removes 'devices', for
> > simplicity. Then we would have more freedom when extending the
> > schema later.
> 
> Yes, please.
> 
> >> >> >> > +#
> >> >> >> > +# @bus: ID of the bus object where the device can be plugged. Optional,
> >> >> >> > +#       as some devices don't need a bus to be plugged (e.g. CPUs).
> >> >> >> > +#       Can be copied to the "bus" argument to @device_add.
> >> >> >> 
> >> >> >> Suggest something like "Can be used as value for @device_add's bus
> >> >> >> option".
> >> >> >
> >> >> > Will do.
> >> >> >
> >> >> >> 
> >> >> >> Should we give similar information on the slot address?  The option name
> >> >> >> is obvious.  What about acceptable values?
> >> >> >
> >> >> > Actually, this part of the documentation was a leftover from a
> >> >> > previous version where @bus was inside DeviceSlotInfo. Now I
> >> >> > moved @bus inside @props for all device types, and it should be
> >> >> > documented the same way as the other fields inside @props.
> >> >> >
> >> >> >> 
> >> >> >> > +#
> >> >> >> > +# @type: type of device slot.
> >> >> >> > +#
> >> >> >> > +# @accepted-device-types: List of device types accepted by the slot.
> >> >> >> > +#                         Any device plugged to the slot should implement
> >> >> >> > +#                         one of the accepted device types.
> >> >> >> > +#
> >> >> >> > +# @max-devices: #optional maximum number of devices that can be plugged
> >> >> >> > +#               to the slot.
> >> >> >> 
> >> >> >> What does it mean when @max-devices isn't given?
> >> >> >
> >> >> > It means there is no hard limit on the number of pluggable
> >> >> > devices. sysbus is one of such cases.
> >> >> 
> >> >> Sysbus certainly has limits, but they're more complicated than just "at
> >> >> most @max-devices devices".
> >> >> 
> >> >> >> > +#
> >> >> >> > +# @devices: List of QOM paths for devices that are already plugged.
> >> >> >> > +#
> >> >> >> > +# @available: If false, the slot is not available for plugging any device.
> >> >> >> > +#             This value can change at runtime if condition changes
> >> >> >> > +#             (e.g. if the device becomes full, or if the machine
> >> >> >> > +#             was already initialized and the slot doesn't support
> >> >> >> > +#             hotplug).
> >> >> >> > +#
> >> >> >> > +# @hotpluggable: If true, the slot accepts hotplugged devices.
> >> >> >> > +#
> >> >> >> > +# DeviceSlotInfo structs always have a @props member, whose members
> >> >> >> > +# can be directly copied to the arguments to @device_add.
> >> >> >> 
> >> >> >> Do you mean names of properties common to all @accepted-device-types?
> >> >> >
> >> >> > I mean that it should be safe to simply use the keys+values in
> >> >> > @props as arguments to device_add or -device, for any slot @type.
> >> >> > Some clients may want to extract some information from @props (if
> >> >> > they already manage PCI addresses, for example), but some of them
> >> >> > may simply choose any available slot and copy @props blindly.
> >> >> >
> >> >> > I didn't want to state anything about QOM properties, because I
> >> >> > only want @props to represent device_add/-device arguments. Some
> >> >> > of those arguments are consumed by the device_add code itself
> >> >> > (e.g. "bus") and others are translated to QOM properties. I don't
> >> >> > want the interface to impose any restrictions on how this is
> >> >> > implemented.
> >> >> 
> >> >> Hmm, is this meant to work as follows?  To plug a device into this slot,
> >> >> you use
> >> >> 
> >> >>     { "execute": "device_add",
> >> >>       "arguments": { "driver": TYPE, ... PROPS ... }
> >> >> 
> >> >> where TYPE is a (concrete subtype of a) member of
> >> >> @accepted-device-types, and PROPS is the value of @props spliced in.
> >> >> Example: the data for slot 06.0 of PCI bus pci.0 would be something like
> >> >> 
> >> >>     {
> >> >>       "bus": "pci.0",
> >> >
> >> > There's no "bus" field in DeviceSlotInfo. It was a documentation bug.
> >> 
> >> Should read code, not docs ;)
> >> 
> >> >>       "props": {
> >> >>           "bus": "pci.0",
> >> >>           "addr": "06.0"
> >> >>       }
> >> >>     }
> >> >> 
> >> >> Doesn't match your example output; I guess I'm still misunderstanding
> >> >> something.
> >> >
> >> > This is a busy PCI slot, from my example:
> >> >
> >> >         {
> >> >             "available": false,
> >> >             "devices": [
> >> >                 "/machine/q35/mch"
> >> >             ],
> >> >             "accepted-device-types": [
> >> >                 "legacy-pci-device",
> >> >                 "pci-express-device"
> >> >             ],
> >> >             "props": {
> >> >                 "bus": "/machine/q35/pcie.0",
> >> 
> >> Does device_add device_add bus=/machine/q35/pcie.0 even work?  I know
> >> qbus_find() interprets /-separated paths, but their semantics are FUBAR
> >> (I can find details if you're curious).  We've long advised to use only
> >> values without '/', like bus=pcie.0.
> >
> > @device_add documentation says:
> >
> > # @bus: #optional the device's parent bus (device tree path)
> 
> The line is a recent addition (commit 94cfd07).  Note "device tree
> path", not "QOM path".  I think it should either not mention paths at
> all, or advise people to stick to qdev IDs.
> 
> >> Now, QOM gives us a framework for interpreting paths sanely.  Switching
> >> the value of bus to QOM paths would be a compatibility break, though.
> >> Matters only if the botched /-paths are actually used.
> >
> > I would be happy to use the bus name instead of the full path,
> > but I don't see any code that ensures that bus names are really
> > unique. Is there any existing mechanism to ensure that?
> 
> Kind of.  qbus names are typically derived from the providing device's
> qdev ID, which is unique.  When they're not, clashes *are* possible.  We
> used to have one with -m isapc: the two IDE buses are provided by
> separate isa-ide qdevs, and both qbuses were named ide.0.  Fixed in
> commit 61de36.  Now the first one to register becomes ide.0, and the
> second one ide.1.  I'm not fond of such implicit naming, but we got
> bigger fish to fry.
> 
> Perhaps we should sidestep this mess and use QOM paths.

Maybe, but I would like to make the returned information fit what
libvirt already uses in its internal address-space allocation
logic.

If we return "pci.0", libvirt will understand what we are talking
about because they already have existing code that assumes
there's a bus named "pci.0" in some machine-types. If we return a
QOM path like "/machine/i440fx/pci.0", they can't be sure we are
really talking about the same thing (unless we specify
QOM path <=> bus name mapping rules very carefully).

> 
> >> >                 "addr": 0
> >> >             },
> >> >             "hotpluggable": false,
> >> >             "type": "pci",
> >> >             "max-devices": 1
> >> >         },
> [...]
Laine Stump Dec. 15, 2016, 7:15 p.m. UTC | #14
On 12/15/2016 05:12 AM, Marcel Apfelbaum wrote:
> On 12/14/2016 10:10 PM, Laine Stump wrote:
>> On 12/14/2016 12:17 PM, Marcel Apfelbaum wrote:
>>> Be aware that 1 slot doesn't necessarily refer to only one device,
>>> we can have a multi-function device.
>>
>> Is that a fixed thing? I mean, is it always possible to use all 8
>> functions of a slot (assuming no hotplug of course)?
>>
>
> Yes, you simply add to the function 0 on command line:  with -device
> ...,multifunction=on.

Yes, I know how the commandline works. my question is whether *all* PCI 
controllers in existence have 8 usable functions per slot, or if that 
might be another thing that is different from one controller to the next 
(and thus would have to be reported in these query responses).
Eduardo Habkost Dec. 15, 2016, 7:33 p.m. UTC | #15
On Thu, Dec 15, 2016 at 02:15:51PM -0500, Laine Stump wrote:
> On 12/15/2016 05:12 AM, Marcel Apfelbaum wrote:
> > On 12/14/2016 10:10 PM, Laine Stump wrote:
> > > On 12/14/2016 12:17 PM, Marcel Apfelbaum wrote:
> > > > Be aware that 1 slot doesn't necessarily refer to only one device,
> > > > we can have a multi-function device.
> > > 
> > > Is that a fixed thing? I mean, is it always possible to use all 8
> > > functions of a slot (assuming no hotplug of course)?
> > > 
> > 
> > Yes, you simply add to the function 0 on command line:  with -device
> > ...,multifunction=on.
> 
> Yes, I know how the commandline works. my question is whether *all* PCI
> controllers in existence have 8 usable functions per slot, or if that might
> be another thing that is different from one controller to the next (and thus
> would have to be reported in these query responses).

That's a good question. From looking at the code, I was assuming
the 8 functions were always available. But if they might be not,
that's another reason to explicitly report PCI functions in the
new queries.
Markus Armbruster Dec. 16, 2016, 9:36 a.m. UTC | #16
Laine Stump <laine@redhat.com> writes:

> On 12/15/2016 05:12 AM, Marcel Apfelbaum wrote:
>> On 12/14/2016 10:10 PM, Laine Stump wrote:
>>> On 12/14/2016 12:17 PM, Marcel Apfelbaum wrote:
>>>> Be aware that 1 slot doesn't necessarily refer to only one device,
>>>> we can have a multi-function device.
>>>
>>> Is that a fixed thing? I mean, is it always possible to use all 8
>>> functions of a slot (assuming no hotplug of course)?
>>>
>>
>> Yes, you simply add to the function 0 on command line:  with -device
>> ...,multifunction=on.
>
> Yes, I know how the commandline works. my question is whether *all*
> PCI controllers in existence have 8 usable functions per slot, or if
> that might be another thing that is different from one controller to
> the next (and thus would have to be reported in these query
> responses).

Quote from PCI Express Base Specification Revision 3.0: "Each non-ARI
Device may contain up to eight Functions.  Each ARI Device may contain
up to 256 Functions."

The address format is defined by the PCI spec: bus number is 8 bits,
device number is 5, function number is 3.

However, a device may be an ARI device (ARI = Alternative Routing-ID
Interpretation), and then the device number bits are instead used for a
wider function number.  As far as I know, this is PCIe-only.  See PCI
Express Base Specification Revision 3.0, 6.13. Alternative Routing-ID
Interpretation (ARI).
Markus Armbruster Dec. 16, 2016, 10:37 a.m. UTC | #17
Eduardo Habkost <ehabkost@redhat.com> writes:

> On Wed, Dec 14, 2016 at 09:11:10PM +0100, Markus Armbruster wrote:
>> Eduardo Habkost <ehabkost@redhat.com> writes:
>> 
>> > On Wed, Dec 14, 2016 at 04:34:04PM +0100, Markus Armbruster wrote:
>> >> Eduardo Habkost <ehabkost@redhat.com> writes:
>> >> 
>> >> > On Tue, Dec 13, 2016 at 08:51:34PM +0100, Markus Armbruster wrote:
>> >> >> Eduardo Habkost <ehabkost@redhat.com> writes:
>> >> >> 
>> >> >> > On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
>> >> >> >> Quick interface review only:
>> >> >> >> 
>> >> >> >> Eduardo Habkost <ehabkost@redhat.com> writes:
>> >> >> >> 
>> >> >> >> > This adds a new command to QMP: query-device-slots. It will allow
>> >> >> >> > management software to query possible slots where devices can be
>> >> >> >> > plugged.
>> >> >> >> >
>> >> >> >> > This implementation of the command will return:
>> >> >> >> >
>> >> >> >> > * Multiple PCI slots per bus, in the case of PCI buses;
>> >> >> >> > * One slot per bus in the case of the other buses;
>> >> >> >> 
>> >> >> >> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
>> >> >> >> slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
>> >> >> >> or two (PATA).  For more examples, see qdev-device-use.txt section
>> >> >> >> "Specifying Bus and Address on Bus".
>> >> >> >
>> >> >> > Yes, I should have clarified that: this version changes only PCI
>> >> >> > to expose multiple slots, but other buses also need be changed to
>> >> >> > implement BusClass::enumerate_slots() properly, too.
>> >> >> >
>> >> >> >> 
>> >> >> >> > * One slot for each entry from query-hotpluggable-cpus.
>> >> >> >> > In the example below, I am not sure if the PCIe ports are all
>> >> >> >> > supposed to report all slots, but I didn't find any existing
>> >> >> >> > field in PCIBus that would help me figure out the actual number
>> >> >> >> > of slots in a given PCI bus.
>> >> >> >> >
>> >> >> >> > Git tree
>> >> >> >> > --------
>> >> >> >> >
>> >> >> >> > This patch needs the previous query-machines series I am working
>> >> >> >> > on. The full tree can be found on the git tree at:
>> >> >> >> >
>> >> >> >> >   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
>> >> >> >> >
>> >> >> >> > Example output
>> >> >> >> > --------------
>> >> >> >> >
>> >> >> >> > The following output was returned by QEMU when running it as:
>> >> >> >> >
>> >> >> >> >  $ qemu-system-x86_64 -machine q35 \
>> >> >> >> >    -readconfig docs/q35-chipset.cfg \
>> >> >> >> >    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
>> >> >> >> >
>> >> >> >> >  {
>> >> >> >> >     "return": [
>> >> >> >> 
>> >> >> >> Are you sure >3000 lines of example output make sense here?
>> >> >> >
>> >> >> > I'm not. :)
>> >> >> >
>> >> >> > That's why I need feedback from the PCI experts. I believe most
>> >> >> > of the PCI ports on q35 accept only one device, but I see no code
>> >> >> > implementing that restriction, and no obvious PCIBus or
>> >> >> > PCIBusClass field indicating that.
>> >> >> >
>> >> >> >> 
>> >> >> >> [...]
>> >> >> >> >     ]
>> >> >> >> >   }
>> >> >> >> >
>> >> >> >> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
>> >> >> >> > ---
>> >> >> >> >  qapi-schema.json       | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
>> >> >> >> >  include/hw/qdev-core.h |   6 +++
>> >> >> >> >  hw/core/bus.c          |  49 +++++++++++++++++++++
>> >> >> >> >  hw/pci/pci.c           | 106 +++++++++++++++++++++++++++++++++------------
>> >> >> >> >  qdev-monitor.c         |  86 ++++++++++++++++++++++++++++++++++---
>> >> >> >> >  5 files changed, 328 insertions(+), 33 deletions(-)
>> >> >> >> >
>> >> >> >> > diff --git a/qapi-schema.json b/qapi-schema.json
>> >> >> >> > index d48ff3f..484d91e 100644
>> >> >> >> > --- a/qapi-schema.json
>> >> >> >> > +++ b/qapi-schema.json
>> >> >> >> > @@ -3166,6 +3166,120 @@
>> >> >> >> >  { 'command': 'closefd', 'data': {'fdname': 'str'} }
>> >> >> >> >  
>> >> >> >> >  ##
>> >> >> >> > +# @DeviceSlotType:
>> >> >> >> > +#
>> >> >> >> > +# Type of device slot
>> >> >> >> > +#
>> >> >> >> > +# @generic: Generic device slot, with no bus-specific information
>> >> >> >> > +# @pci: PCI device slot
>> >> >> >> > +# @cpu: CPU device slot
>> >> >> >> > +##
>> >> >> >> > +{ 'enum': 'DeviceSlotType',
>> >> >> >> > +  'data': ['generic', 'pci', 'cpu'] }
>> >> >> >> > +
>> >> >> >> > +##
>> >> >> >> > +# @DeviceSlotInfo:
>> >> >> >> > +#
>> >> >> >> > +# Information on a slot where devices can be plugged. Some buses
>> >> >> >> > +# are represented as a single slot that can support multiple devices,
>> >> >> >> > +# and some buses have multiple slots that are identified by arguments
>> >> >> >> > +# to @device_add.
>> >> >> >> 
>> >> >> >> Okay, let me try to wrap my poor, ignorant mind around this.
>> >> >> >> 
>> >> >> >> There are two kinds of buses: "single slot that can support multiple
>> >> >> >> devices", and "multiple slots that are identified by arguments".
>> >> >> >> 
>> >> >> >> How are the two kinds related to @type?
>> >> >> >
>> >> >> > They are related to @type indirectly because different bus types
>> >> >> > will return different data. But I don't want to make this part of
>> >> >> > the specification: clients should be prepared to handle both
>> >> >> > cases.
>> >> >> 
>> >> >> Well, color me confused :)
>> >> >> 
>> >> >> > e.g. a QEMU version might return a single generic catch-all
>> >> >> > sysbus-device slot that support any number of devices. Future
>> >> >> > versions may return more detailed information, and return slots
>> >> >> > only for the sysbus devices that really work with the machine.
>> >> >> 
>> >> >> Hmm.  See below.
>> >> >> 
>> >> >> >> 
>> >> >> >> Examples for "multiple slots that are identified by arguments":
>> >> >> >> 
>> >> >> >>     -device edu,addr=06.0,bus=...
>> >> >> >>     -device scsi-hd,scsi-hd=5,bus=...
>> >> >> >>     -device ide-hd,unit=1,bus=...
>> >> >> >>     -device virtserialport,nr=7,bus=...
>> >> >> >> 
>> >> >> >> Note that each of these buses has its own way to specify a slot address,
>> >> >> >> namely a bus-specific option.
>> >> >> >
>> >> >> > Yes.
>> >> >> >
>> >> >> >> 
>> >> >> >> Can you give examples for "single slot that can support multiple
>> >> >> >> devices"?
>> >> >> >
>> >> >> > I can't name any example except sysbus, right now.
>> >> >> 
>> >> >> Sysbus is a bad example, because it's a hack, not a bus.
>> >> >> 
>> >> >> Physical devices are wired up in some device-specific way.  An important
>> >> >> special wiring case is plugging into a standard socket provided by a
>> >> >> bus.  But not every device is connected only to a bus.  Devices can also
>> >> >> be wired to other devices in ad hoc ways.
>> >> >> 
>> >> >> In the initial qdev design, devices could only plug into buses.
>> >> >> Anything that didn't fit the mold was declared to plug into the "system
>> >> >> bus", which isn't a bus at all.
>> >> >> 
>> >> >> The hack used to be fairly harmless, because you couldn't do much with
>> >> >> system bus devices anyway.  Not true anymore: Alex created means to add
>> >> >> certain system bus devices to certain machines with -device.  Has been a
>> >> >> thorn in my side ever since.  I'm afraid we need to understand how
>> >> >> exactly it works before we can finalize the design for this command.
>> >> >
>> >> > I agree it is a hack, I just wanted to be able to cover it too
>> >> > (and possibly other cases where there's no obvious "address"
>> >> > parameter to devices).
>> >> >
>> >> > The other options I had were:
>> >> > a) Omitting the bus from the output;
>> >> > b) Waiting until we fix sysbus before implementing the interface.
>> >> >
>> >> > I avoided (a) because I would like to tell libvirt "always check
>> >> > query-device-slots before plugging anything. if it's not there,
>> >> > it means you can't plug it". To do that, I would need to ensure
>> >> > all buses are present in the list (even sysbus).
>> >> >
>> >> > (But as I say below: I don't love this solution and I am open to
>> >> > suggestions).
>> >> 
>> >> I'm okay with doing (c) including all buses, with full information for
>> >> only some.  As long as the case "no full information" is clearly
>> >> separate, and appropriately documented.  Then we can tell libvirt to
>> >> always check query-device-slots, and if it has full information, use
>> >> that.  Else, falling back to hardcoded strategies is okay, similar to
>> >> what's done when command query-device-slots doesn't exist.
>> >
>> > Understood.
>> >
>> >> 
>> >> >> > There are two cases that I tried to cover by reporting
>> >> >> > multiple-device no-argument slots:
>> >> >> >
>> >> >> > 1) Buses that were not converted (yet) to implement
>> >> >> >    enumerate_buses() (in the current version: everything except
>> >> >> >    PCI)
>> >> >> > 2) Buses that really do not require any extra slot address
>> >> >> >    argument (sysbus? others?)
>> >> >> >
>> >> >> > I'm proposing this interface because I don't want (1) or (2) to
>> >> >> > be missing from the returned data (otherwise management software
>> >> >> > can't differentiate "this bus is not available" from "this bus
>> >> >> > simply doesn't implement slot enumeration yet").
>> >> >> >
>> >> >> > We won't need the special multi-device-slot case if we eliminate
>> >> >> > all instances of (1) and (2). Can we do that in 2.9? I'm not
>> >> >> > sure.
>> >> >> 
>> >> >> If we can't do a complete job, and we don't want to hide slots affected
>> >> >> by that, the data we return for them should unequivocally state that
>> >> >> it's incomplete because computing complete data hasn't been implemented,
>> >> >> yet.
>> >> >
>> >> > OK, so we agree we need a way to tell the client the data is
>> >> > incomplete (or not as detailed as we would like).
>> >> 
>> >> Yes.
>> >> 
>> >> > I tried to solve that by providing a mechanism to tell the client
>> >> > "look, I can tell you that there's a bus called <bus name>, and
>> >> > it is valid to use it as the 'bus' argument for -device and
>> >> > device_add, but I don't know the rest of the rules (sorry!)". We
>> >> > can do that by simply including the bus on the list as if it was
>> >> > a multi-device slot.
>> >> 
>> >> What if we ever run into a kind of slot where the full information is
>> >> "use this 'bus' argument" and "you can plug in multiple devices"?
>> >> Wouldn't that be indistinguishable from "full information isn't
>> >> available"?
>> >> 
>> >> Let's make "full information isn't available" impossible to confuse with
>> >> any conceivable set of full information.
>> >
>> > I see. I tried to model it in a way that "incomplete information"
>> > was not considered an exception, just an instance where the data
>> > is less informative. But adding another bit of information that
>> > explicitly says "this in incomplete and will probably change in
>> > the future" is a good idea.
>> >
>> >> 
>> >> > But I don't love this solution, so I am open to other suggestions
>> >> > on how to tell the client that slot information is incomplete for
>> >> > a bus.
>> >> >
>> >> > Maybe the following?
>> >> >
>> >> > 1) Add a new DeviceSlotType value meaning "this entry represents
>> >> >    a bus/device-type that can't enumerate its slots" (let's call
>> >> >    it "bus-with-no-slot-info" by now).
>> >> 
>> >> Let's call this one a non-slot, for brevity.
>> >
>> > I like that name. :)
>> >
>> >> 
>> >> > 2) Remove DeviceSlotType "generic".
>> >> > 3) Remove max-devices field, and simply document the device-limit
>> >> >    semantics for each DeviceSlotType (cpu: 1 device, pci: 1
>> >> >    device, bus-with-no-slot-info: undefined limit).
>> >> 
>> >> Not sure there's much to document.  For me a slot takes exactly one plug
>> >> by definition (if it can take more, it's a set of slots, isn't it?), and
>> >> non-slots take an unknown number of plugs by definition (if we knew,
>> >> we'd return suitable slots instead).  But that's mincing words; I'm sure
>> >> we can come up with language that works for both of us.
>> >
>> > I am trying to not make any guarantees about all slot types,
>> > until we learn more about the more complex cases. All we can say
>> > right now is that CPU and PCI slots support only 1 device, and we
>> > don't claim anything about others.
>> 
>> Sane physical buses generally have a "device address" concept: a device
>> on the bus is identified by its (unique) device address.  For instance,
>> SCSI has scsi-id, and PCI has device number.
>> 
>> Less-than-sane buses exist, such as the (pre-PnP) ISA "bus": there's no
>> device address that satisfies my definition.
>> 
>> > I'm not even 100% sure about the 1-device rule for PCI, as each
>> > PCI function in a slot is a separate DeviceState*. I need to
>> > understand how multi-function PCI hotplug works, to find out what
>> > we should do.
>> 
>> *Real* PCI devices consist of function 0 and optionally additional
>> functions.  The device gets plugged as a whole (d'oh) into a slot with a
>> specific device address.  Note that the dev.fn address addresses a
>> *function*, not a device.
>> 
>> The way QEMU models this is "madness, yet there is method in't".  A PCI
>> qdev models a PCI *function*, not a PCI device.  It gets plugged into a
>> PCI qbus "slot" with a specific *function* address (dev.fn).  The
>> functions that share the dev part of the function address together form
>> a PCI device.  You have to plug something into function 0 for this to
>> work.  Sufficiently weird function combinations might conceivably
>> confuse software.  If I remember correctly, you have to hot-plug
>> function 0 last (actually triggers the device hot-plug), and
>> hot-unplugging function 0 yanks out all the functions.
>> 
>> When a device has just one function, which is a fairly common case, then
>> you can pretend the PCI qdev is a PCI device.
>> 
>> So, what are the slots provided by a PCI (not PCIe) bus?  A *real* one
>> provides 32 *device* slots.  A qbus, however, provides 256 *function*
>> "slots".
>> 
>> How should query-device-slots report this?  All 256 function "slots", or
>> just the 32 function 0 slots?  Since management software needs to know
>> about the PCI qdev madness anyway, I'd say just the 32.
>
> So, if weach PCI function is a separate device for QEMU, isn't
> this breaking your suggested rule about each slot supporting only
> 1 device?

They all go into separate slots.  Evidence: each one needs its own
addr=..., e.g. addr=6.0, addr=6.1, ...

The trouble is that the slots where function is non-zero aren't real,
but an artifact of the weird way we model PCI.

>> Same question for PCIe, with and without ARI.
>> 
>> If we had had QOM back when we made this mess, we'd have designed PCI
>> devices as a composition of PCI functions.  At least I hope we'd have.
>> With such a design, we'd assemble functions into a device, then plug the
>> device.  A slot would then be the same as for a real bus.
>> 
>> > I worry about having a general 1-slot = 1-device rule from the
>> > beginning, because I don't know if it can be a problem when the
>> > set of possible slots is too large. e.g.: should we return one
>> > slot for each of the 256 possible devfns in a PCI bus? What if a
>> > bus uses 32-bit identifiers for devices?
>> >
>> > My suggestion is to avoid any general claims about device limits
>> > in slots in the documentation by now, until we have more
>> > knowledge about other bus types.
>> 
>> For me, a slot can't take more than one device.  Something that can
>> isn't a slot, it's a set of slots.
>
> So, is a PCI device number (that supports up to 8 functions, each
> with its own device object) a slot, or a set of slots?

For real hardware, the device number identifies the slot.

In our weirdly modelled virtual world, we can pick between two
alternatives for query-device-slots:

1. Follow real hardware, and expose only real slots.  Management
   applications supporting multi-function need to know that each real
   slot is associated with a bunch of "unreal" slots, and that they need
   to plug function 0 into the real slot (where it becomes the device),
   and any other functions into associated unreal slots.

   Note that the .FN part in addr=DEV.FN is optional.  We could exploit
   that and let query-device-slots pretend that the value of addr is a
   number between 0 and 31.

2. Screw real hardware, and expose real and unreal slots.  Management
   applications need to know that unreal slots are only usable together
   with their real slot.  Even when they only deal with single-function
   devices.

Either way, management applications need to know more about our PCI bus
type than about saner bus types.

A possible third alternative would be to create a "set of related slots
that can only be used together in a certain way (which I can't explain
to you)" abstraction, but I'm afraid that'll be basically the same as 1,
only more complicated.

I'm leaning towards 1, because it's closer to real hardware, and I hope
it requires slightly less ugliness in management applications.

>> Comitting to a design now that models this as a "slot" that can take
>> multiple devices seems premature.  We really have no idea whether that
>> would work!
>> 
>> But you made a good point: set of slots too large to permit
>> query-device-slots enumerating its members may exist.
>> 
>> Enumerating members is just one way to describe a set.  An algorithm to
>> generate the members is another.
>> 
>> Let's try for your hypothetical bus with 2^32 slots, each identified by
>> an unsigned 32-bit number.  Say the syntax is bus=BUSNAME,addr=UINT32.
>> We need to describe bus=BUSNAME,addr=UINT32.  The bus=BUSNAME part is
>> simple enough: "props" contains a member "bus": "BUSNAME".  For the
>> addr=UINT32 part, we'll have to invent suitable language to express "any
>> integer between 0 and 2^32-1".
>> 
>> I believe this will be easier when the information for all slots is
>> otherwise identical.  If it is, we can describe all 2^32 slots in one
>> dictionary.  But if slot#7 and slot#42..666 are different, we need
>> multiple, and probably a more complex language for slot addresses, to
>> express things like "any integer between 0 and 2^32-1, except for 7 and
>> 42..666".

A possible alternative is cascading: combine all entries covering a
slot.

> The question for me is: do we need it now, or can we add this as
> an extension in a future version?

I think we need to explore much of the problem space, then solve a
useful part of the problem in a simple way.  The exploration hopefully
enables us to design a simple solution that can actually grow into a
more complete solution.

>> Note that including information on currently plugged devices makes slot
>> information different.
>> 
>> Let's take a step back and consider what we want to achieve with
>> query-device-slots.  I think the core objective is to let libvirt know:
>> 
>> * What slots exist
>> 
>> * What devices can be plugged into each slot
>> 
>> * Whether plugging / unplugging is possible right now
>> 
>> * What arguments need to be used to plug something into a specific slot
>> 
>> Your RFC design provides additional information, namely
>> 
>> * Currently plugged devices
>> 
>>   I'd expect this to be redundant with other queries, such as QOM
>>   introspection.
>> 
>> * Whether hot-plug is possible
>> 
>>   If the machine has been started already, this is the same as whether
>>   plugging / unplugging is possible right now.
>> 
>>   Else, it permits predicting whether it will be possible once the
>>   machine is started.  Perhaps that's important to know, perhaps not.
>> 
>> * Perhaps (I'm not sure) even internal-only slots, i.e. ones that can
>>   never be used with device_add.
>> 
>> Let's focus on the core objective and completely ignore the additional
>> information for now.
>> 
>> >> > This looks like a trade-off between moving data to specific union
>> >> > types (and representing type-specific limits/rules in the schema
>> >> > documentation), or moving data to the base union type (and
>> >> > representing type-specific limits as data in the base union
>> >> > type).
>> >> 
>> >> Works for me.
>> >> 
>> >> Additionally, let's replace 'devices': [ 'str' ] by '*device': 'str',
>> >> because a slot is either empty or has one device plugged in.
>> >> 
>> >> If we ever need to add a set-of-slots type, we'll have to extend the
>> >> schema, but then we'll be in a much better position to know what we
>> >> actually need.
>> >> 
>> >
>> > I am working on a version that even removes 'devices', for
>> > simplicity. Then we would have more freedom when extending the
>> > schema later.
>> 
>> Yes, please.
>> 
>> >> >> >> > +#
>> >> >> >> > +# @bus: ID of the bus object where the device can be plugged. Optional,
>> >> >> >> > +#       as some devices don't need a bus to be plugged (e.g. CPUs).
>> >> >> >> > +#       Can be copied to the "bus" argument to @device_add.
>> >> >> >> 
>> >> >> >> Suggest something like "Can be used as value for @device_add's bus
>> >> >> >> option".
>> >> >> >
>> >> >> > Will do.
>> >> >> >
>> >> >> >> 
>> >> >> >> Should we give similar information on the slot address?  The option name
>> >> >> >> is obvious.  What about acceptable values?
>> >> >> >
>> >> >> > Actually, this part of the documentation was a leftover from a
>> >> >> > previous version where @bus was inside DeviceSlotInfo. Now I
>> >> >> > moved @bus inside @props for all device types, and it should be
>> >> >> > documented the same way as the other fields inside @props.
>> >> >> >
>> >> >> >> 
>> >> >> >> > +#
>> >> >> >> > +# @type: type of device slot.
>> >> >> >> > +#
>> >> >> >> > +# @accepted-device-types: List of device types accepted by the slot.
>> >> >> >> > +#                         Any device plugged to the slot should implement
>> >> >> >> > +#                         one of the accepted device types.
>> >> >> >> > +#
>> >> >> >> > +# @max-devices: #optional maximum number of devices that can be plugged
>> >> >> >> > +#               to the slot.
>> >> >> >> 
>> >> >> >> What does it mean when @max-devices isn't given?
>> >> >> >
>> >> >> > It means there is no hard limit on the number of pluggable
>> >> >> > devices. sysbus is one of such cases.
>> >> >> 
>> >> >> Sysbus certainly has limits, but they're more complicated than just "at
>> >> >> most @max-devices devices".
>> >> >> 
>> >> >> >> > +#
>> >> >> >> > +# @devices: List of QOM paths for devices that are already plugged.
>> >> >> >> > +#
>> >> >> >> > +# @available: If false, the slot is not available for plugging any device.
>> >> >> >> > +#             This value can change at runtime if condition changes
>> >> >> >> > +#             (e.g. if the device becomes full, or if the machine
>> >> >> >> > +#             was already initialized and the slot doesn't support
>> >> >> >> > +#             hotplug).
>> >> >> >> > +#
>> >> >> >> > +# @hotpluggable: If true, the slot accepts hotplugged devices.
>> >> >> >> > +#
>> >> >> >> > +# DeviceSlotInfo structs always have a @props member, whose members
>> >> >> >> > +# can be directly copied to the arguments to @device_add.
>> >> >> >> 
>> >> >> >> Do you mean names of properties common to all @accepted-device-types?
>> >> >> >
>> >> >> > I mean that it should be safe to simply use the keys+values in
>> >> >> > @props as arguments to device_add or -device, for any slot @type.
>> >> >> > Some clients may want to extract some information from @props (if
>> >> >> > they already manage PCI addresses, for example), but some of them
>> >> >> > may simply choose any available slot and copy @props blindly.
>> >> >> >
>> >> >> > I didn't want to state anything about QOM properties, because I
>> >> >> > only want @props to represent device_add/-device arguments. Some
>> >> >> > of those arguments are consumed by the device_add code itself
>> >> >> > (e.g. "bus") and others are translated to QOM properties. I don't
>> >> >> > want the interface to impose any restrictions on how this is
>> >> >> > implemented.
>> >> >> 
>> >> >> Hmm, is this meant to work as follows?  To plug a device into this slot,
>> >> >> you use
>> >> >> 
>> >> >>     { "execute": "device_add",
>> >> >>       "arguments": { "driver": TYPE, ... PROPS ... }
>> >> >> 
>> >> >> where TYPE is a (concrete subtype of a) member of
>> >> >> @accepted-device-types, and PROPS is the value of @props spliced in.
>> >> >> Example: the data for slot 06.0 of PCI bus pci.0 would be something like
>> >> >> 
>> >> >>     {
>> >> >>       "bus": "pci.0",
>> >> >
>> >> > There's no "bus" field in DeviceSlotInfo. It was a documentation bug.
>> >> 
>> >> Should read code, not docs ;)
>> >> 
>> >> >>       "props": {
>> >> >>           "bus": "pci.0",
>> >> >>           "addr": "06.0"
>> >> >>       }
>> >> >>     }
>> >> >> 
>> >> >> Doesn't match your example output; I guess I'm still misunderstanding
>> >> >> something.
>> >> >
>> >> > This is a busy PCI slot, from my example:
>> >> >
>> >> >         {
>> >> >             "available": false,
>> >> >             "devices": [
>> >> >                 "/machine/q35/mch"
>> >> >             ],
>> >> >             "accepted-device-types": [
>> >> >                 "legacy-pci-device",
>> >> >                 "pci-express-device"
>> >> >             ],
>> >> >             "props": {
>> >> >                 "bus": "/machine/q35/pcie.0",
>> >> 
>> >> Does device_add device_add bus=/machine/q35/pcie.0 even work?  I know
>> >> qbus_find() interprets /-separated paths, but their semantics are FUBAR
>> >> (I can find details if you're curious).  We've long advised to use only
>> >> values without '/', like bus=pcie.0.
>> >
>> > @device_add documentation says:
>> >
>> > # @bus: #optional the device's parent bus (device tree path)
>> 
>> The line is a recent addition (commit 94cfd07).  Note "device tree
>> path", not "QOM path".  I think it should either not mention paths at
>> all, or advise people to stick to qdev IDs.
>> 
>> >> Now, QOM gives us a framework for interpreting paths sanely.  Switching
>> >> the value of bus to QOM paths would be a compatibility break, though.
>> >> Matters only if the botched /-paths are actually used.
>> >
>> > I would be happy to use the bus name instead of the full path,
>> > but I don't see any code that ensures that bus names are really
>> > unique. Is there any existing mechanism to ensure that?
>> 
>> Kind of.  qbus names are typically derived from the providing device's
>> qdev ID, which is unique.  When they're not, clashes *are* possible.  We
>> used to have one with -m isapc: the two IDE buses are provided by
>> separate isa-ide qdevs, and both qbuses were named ide.0.  Fixed in
>> commit 61de36.  Now the first one to register becomes ide.0, and the
>> second one ide.1.  I'm not fond of such implicit naming, but we got
>> bigger fish to fry.
>> 
>> Perhaps we should sidestep this mess and use QOM paths.
>
> Maybe, but I would like to make the returned information fit what
> libvirt already uses in its internal address-space allocation
> logic.
>
> If we return "pci.0", libvirt will understand what we are talking
> about because they already have existing code that assumes
> there's a bus named "pci.0" in some machine-types. If we return a
> QOM path like "/machine/i440fx/pci.0", they can't be sure we are
> really talking about the same thing (unless we specify
> QOM path <=> bus name mapping rules very carefully).

I think the way to go here at least initially is to document that the
value of bus is not to be interpreted, only passed to device_add as
option "bus".

If ever gets changed to accept QOM paths, we can choose to let
query-device-slots report them.

As long as we report qbus IDs, we should perhaps suppress slots that
can't actually be addressed.  Example: say there's two qbuses named
"ide.0" for some reason.  We then expose only the one you actually get
with device_add bus=ide.0,...

>> 
>> >> >                 "addr": 0
>> >> >             },
>> >> >             "hotpluggable": false,
>> >> >             "type": "pci",
>> >> >             "max-devices": 1
>> >> >         },
>> [...]
Eduardo Habkost Dec. 16, 2016, 1:37 p.m. UTC | #18
On Fri, Dec 16, 2016 at 11:37:00AM +0100, Markus Armbruster wrote:
> Eduardo Habkost <ehabkost@redhat.com> writes:
> 
> > On Wed, Dec 14, 2016 at 09:11:10PM +0100, Markus Armbruster wrote:
> >> Eduardo Habkost <ehabkost@redhat.com> writes:
> >> 
> >> > On Wed, Dec 14, 2016 at 04:34:04PM +0100, Markus Armbruster wrote:
> >> >> Eduardo Habkost <ehabkost@redhat.com> writes:
> >> >> 
> >> >> > On Tue, Dec 13, 2016 at 08:51:34PM +0100, Markus Armbruster wrote:
> >> >> >> Eduardo Habkost <ehabkost@redhat.com> writes:
> >> >> >> 
> >> >> >> > On Tue, Dec 13, 2016 at 12:04:17PM +0100, Markus Armbruster wrote:
> >> >> >> >> Quick interface review only:
> >> >> >> >> 
> >> >> >> >> Eduardo Habkost <ehabkost@redhat.com> writes:
> >> >> >> >> 
> >> >> >> >> > This adds a new command to QMP: query-device-slots. It will allow
> >> >> >> >> > management software to query possible slots where devices can be
> >> >> >> >> > plugged.
> >> >> >> >> >
> >> >> >> >> > This implementation of the command will return:
> >> >> >> >> >
> >> >> >> >> > * Multiple PCI slots per bus, in the case of PCI buses;
> >> >> >> >> > * One slot per bus in the case of the other buses;
> >> >> >> >> 
> >> >> >> >> Umm, that doesn't seem right.  For instance, a SCSI bus has multiple
> >> >> >> >> slots.  The slot address is the SCSI ID.  An IDE bus may have one (SATA)
> >> >> >> >> or two (PATA).  For more examples, see qdev-device-use.txt section
> >> >> >> >> "Specifying Bus and Address on Bus".
> >> >> >> >
> >> >> >> > Yes, I should have clarified that: this version changes only PCI
> >> >> >> > to expose multiple slots, but other buses also need be changed to
> >> >> >> > implement BusClass::enumerate_slots() properly, too.
> >> >> >> >
> >> >> >> >> 
> >> >> >> >> > * One slot for each entry from query-hotpluggable-cpus.
> >> >> >> >> > In the example below, I am not sure if the PCIe ports are all
> >> >> >> >> > supposed to report all slots, but I didn't find any existing
> >> >> >> >> > field in PCIBus that would help me figure out the actual number
> >> >> >> >> > of slots in a given PCI bus.
> >> >> >> >> >
> >> >> >> >> > Git tree
> >> >> >> >> > --------
> >> >> >> >> >
> >> >> >> >> > This patch needs the previous query-machines series I am working
> >> >> >> >> > on. The full tree can be found on the git tree at:
> >> >> >> >> >
> >> >> >> >> >   git://github.com/ehabkost/qemu-hacks.git work/query-machines-bus-info
> >> >> >> >> >
> >> >> >> >> > Example output
> >> >> >> >> > --------------
> >> >> >> >> >
> >> >> >> >> > The following output was returned by QEMU when running it as:
> >> >> >> >> >
> >> >> >> >> >  $ qemu-system-x86_64 -machine q35 \
> >> >> >> >> >    -readconfig docs/q35-chipset.cfg \
> >> >> >> >> >    -smp 4,maxcpus=8,sockets=2,cores=2,threads=2
> >> >> >> >> >
> >> >> >> >> >  {
> >> >> >> >> >     "return": [
> >> >> >> >> 
> >> >> >> >> Are you sure >3000 lines of example output make sense here?
> >> >> >> >
> >> >> >> > I'm not. :)
> >> >> >> >
> >> >> >> > That's why I need feedback from the PCI experts. I believe most
> >> >> >> > of the PCI ports on q35 accept only one device, but I see no code
> >> >> >> > implementing that restriction, and no obvious PCIBus or
> >> >> >> > PCIBusClass field indicating that.
> >> >> >> >
> >> >> >> >> 
> >> >> >> >> [...]
> >> >> >> >> >     ]
> >> >> >> >> >   }
> >> >> >> >> >
> >> >> >> >> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> >> >> >> >> > ---
> >> >> >> >> >  qapi-schema.json       | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
> >> >> >> >> >  include/hw/qdev-core.h |   6 +++
> >> >> >> >> >  hw/core/bus.c          |  49 +++++++++++++++++++++
> >> >> >> >> >  hw/pci/pci.c           | 106 +++++++++++++++++++++++++++++++++------------
> >> >> >> >> >  qdev-monitor.c         |  86 ++++++++++++++++++++++++++++++++++---
> >> >> >> >> >  5 files changed, 328 insertions(+), 33 deletions(-)
> >> >> >> >> >
> >> >> >> >> > diff --git a/qapi-schema.json b/qapi-schema.json
> >> >> >> >> > index d48ff3f..484d91e 100644
> >> >> >> >> > --- a/qapi-schema.json
> >> >> >> >> > +++ b/qapi-schema.json
> >> >> >> >> > @@ -3166,6 +3166,120 @@
> >> >> >> >> >  { 'command': 'closefd', 'data': {'fdname': 'str'} }
> >> >> >> >> >  
> >> >> >> >> >  ##
> >> >> >> >> > +# @DeviceSlotType:
> >> >> >> >> > +#
> >> >> >> >> > +# Type of device slot
> >> >> >> >> > +#
> >> >> >> >> > +# @generic: Generic device slot, with no bus-specific information
> >> >> >> >> > +# @pci: PCI device slot
> >> >> >> >> > +# @cpu: CPU device slot
> >> >> >> >> > +##
> >> >> >> >> > +{ 'enum': 'DeviceSlotType',
> >> >> >> >> > +  'data': ['generic', 'pci', 'cpu'] }
> >> >> >> >> > +
> >> >> >> >> > +##
> >> >> >> >> > +# @DeviceSlotInfo:
> >> >> >> >> > +#
> >> >> >> >> > +# Information on a slot where devices can be plugged. Some buses
> >> >> >> >> > +# are represented as a single slot that can support multiple devices,
> >> >> >> >> > +# and some buses have multiple slots that are identified by arguments
> >> >> >> >> > +# to @device_add.
> >> >> >> >> 
> >> >> >> >> Okay, let me try to wrap my poor, ignorant mind around this.
> >> >> >> >> 
> >> >> >> >> There are two kinds of buses: "single slot that can support multiple
> >> >> >> >> devices", and "multiple slots that are identified by arguments".
> >> >> >> >> 
> >> >> >> >> How are the two kinds related to @type?
> >> >> >> >
> >> >> >> > They are related to @type indirectly because different bus types
> >> >> >> > will return different data. But I don't want to make this part of
> >> >> >> > the specification: clients should be prepared to handle both
> >> >> >> > cases.
> >> >> >> 
> >> >> >> Well, color me confused :)
> >> >> >> 
> >> >> >> > e.g. a QEMU version might return a single generic catch-all
> >> >> >> > sysbus-device slot that support any number of devices. Future
> >> >> >> > versions may return more detailed information, and return slots
> >> >> >> > only for the sysbus devices that really work with the machine.
> >> >> >> 
> >> >> >> Hmm.  See below.
> >> >> >> 
> >> >> >> >> 
> >> >> >> >> Examples for "multiple slots that are identified by arguments":
> >> >> >> >> 
> >> >> >> >>     -device edu,addr=06.0,bus=...
> >> >> >> >>     -device scsi-hd,scsi-hd=5,bus=...
> >> >> >> >>     -device ide-hd,unit=1,bus=...
> >> >> >> >>     -device virtserialport,nr=7,bus=...
> >> >> >> >> 
> >> >> >> >> Note that each of these buses has its own way to specify a slot address,
> >> >> >> >> namely a bus-specific option.
> >> >> >> >
> >> >> >> > Yes.
> >> >> >> >
> >> >> >> >> 
> >> >> >> >> Can you give examples for "single slot that can support multiple
> >> >> >> >> devices"?
> >> >> >> >
> >> >> >> > I can't name any example except sysbus, right now.
> >> >> >> 
> >> >> >> Sysbus is a bad example, because it's a hack, not a bus.
> >> >> >> 
> >> >> >> Physical devices are wired up in some device-specific way.  An important
> >> >> >> special wiring case is plugging into a standard socket provided by a
> >> >> >> bus.  But not every device is connected only to a bus.  Devices can also
> >> >> >> be wired to other devices in ad hoc ways.
> >> >> >> 
> >> >> >> In the initial qdev design, devices could only plug into buses.
> >> >> >> Anything that didn't fit the mold was declared to plug into the "system
> >> >> >> bus", which isn't a bus at all.
> >> >> >> 
> >> >> >> The hack used to be fairly harmless, because you couldn't do much with
> >> >> >> system bus devices anyway.  Not true anymore: Alex created means to add
> >> >> >> certain system bus devices to certain machines with -device.  Has been a
> >> >> >> thorn in my side ever since.  I'm afraid we need to understand how
> >> >> >> exactly it works before we can finalize the design for this command.
> >> >> >
> >> >> > I agree it is a hack, I just wanted to be able to cover it too
> >> >> > (and possibly other cases where there's no obvious "address"
> >> >> > parameter to devices).
> >> >> >
> >> >> > The other options I had were:
> >> >> > a) Omitting the bus from the output;
> >> >> > b) Waiting until we fix sysbus before implementing the interface.
> >> >> >
> >> >> > I avoided (a) because I would like to tell libvirt "always check
> >> >> > query-device-slots before plugging anything. if it's not there,
> >> >> > it means you can't plug it". To do that, I would need to ensure
> >> >> > all buses are present in the list (even sysbus).
> >> >> >
> >> >> > (But as I say below: I don't love this solution and I am open to
> >> >> > suggestions).
> >> >> 
> >> >> I'm okay with doing (c) including all buses, with full information for
> >> >> only some.  As long as the case "no full information" is clearly
> >> >> separate, and appropriately documented.  Then we can tell libvirt to
> >> >> always check query-device-slots, and if it has full information, use
> >> >> that.  Else, falling back to hardcoded strategies is okay, similar to
> >> >> what's done when command query-device-slots doesn't exist.
> >> >
> >> > Understood.
> >> >
> >> >> 
> >> >> >> > There are two cases that I tried to cover by reporting
> >> >> >> > multiple-device no-argument slots:
> >> >> >> >
> >> >> >> > 1) Buses that were not converted (yet) to implement
> >> >> >> >    enumerate_buses() (in the current version: everything except
> >> >> >> >    PCI)
> >> >> >> > 2) Buses that really do not require any extra slot address
> >> >> >> >    argument (sysbus? others?)
> >> >> >> >
> >> >> >> > I'm proposing this interface because I don't want (1) or (2) to
> >> >> >> > be missing from the returned data (otherwise management software
> >> >> >> > can't differentiate "this bus is not available" from "this bus
> >> >> >> > simply doesn't implement slot enumeration yet").
> >> >> >> >
> >> >> >> > We won't need the special multi-device-slot case if we eliminate
> >> >> >> > all instances of (1) and (2). Can we do that in 2.9? I'm not
> >> >> >> > sure.
> >> >> >> 
> >> >> >> If we can't do a complete job, and we don't want to hide slots affected
> >> >> >> by that, the data we return for them should unequivocally state that
> >> >> >> it's incomplete because computing complete data hasn't been implemented,
> >> >> >> yet.
> >> >> >
> >> >> > OK, so we agree we need a way to tell the client the data is
> >> >> > incomplete (or not as detailed as we would like).
> >> >> 
> >> >> Yes.
> >> >> 
> >> >> > I tried to solve that by providing a mechanism to tell the client
> >> >> > "look, I can tell you that there's a bus called <bus name>, and
> >> >> > it is valid to use it as the 'bus' argument for -device and
> >> >> > device_add, but I don't know the rest of the rules (sorry!)". We
> >> >> > can do that by simply including the bus on the list as if it was
> >> >> > a multi-device slot.
> >> >> 
> >> >> What if we ever run into a kind of slot where the full information is
> >> >> "use this 'bus' argument" and "you can plug in multiple devices"?
> >> >> Wouldn't that be indistinguishable from "full information isn't
> >> >> available"?
> >> >> 
> >> >> Let's make "full information isn't available" impossible to confuse with
> >> >> any conceivable set of full information.
> >> >
> >> > I see. I tried to model it in a way that "incomplete information"
> >> > was not considered an exception, just an instance where the data
> >> > is less informative. But adding another bit of information that
> >> > explicitly says "this in incomplete and will probably change in
> >> > the future" is a good idea.
> >> >
> >> >> 
> >> >> > But I don't love this solution, so I am open to other suggestions
> >> >> > on how to tell the client that slot information is incomplete for
> >> >> > a bus.
> >> >> >
> >> >> > Maybe the following?
> >> >> >
> >> >> > 1) Add a new DeviceSlotType value meaning "this entry represents
> >> >> >    a bus/device-type that can't enumerate its slots" (let's call
> >> >> >    it "bus-with-no-slot-info" by now).
> >> >> 
> >> >> Let's call this one a non-slot, for brevity.
> >> >
> >> > I like that name. :)
> >> >
> >> >> 
> >> >> > 2) Remove DeviceSlotType "generic".
> >> >> > 3) Remove max-devices field, and simply document the device-limit
> >> >> >    semantics for each DeviceSlotType (cpu: 1 device, pci: 1
> >> >> >    device, bus-with-no-slot-info: undefined limit).
> >> >> 
> >> >> Not sure there's much to document.  For me a slot takes exactly one plug
> >> >> by definition (if it can take more, it's a set of slots, isn't it?), and
> >> >> non-slots take an unknown number of plugs by definition (if we knew,
> >> >> we'd return suitable slots instead).  But that's mincing words; I'm sure
> >> >> we can come up with language that works for both of us.
> >> >
> >> > I am trying to not make any guarantees about all slot types,
> >> > until we learn more about the more complex cases. All we can say
> >> > right now is that CPU and PCI slots support only 1 device, and we
> >> > don't claim anything about others.
> >> 
> >> Sane physical buses generally have a "device address" concept: a device
> >> on the bus is identified by its (unique) device address.  For instance,
> >> SCSI has scsi-id, and PCI has device number.
> >> 
> >> Less-than-sane buses exist, such as the (pre-PnP) ISA "bus": there's no
> >> device address that satisfies my definition.
> >> 
> >> > I'm not even 100% sure about the 1-device rule for PCI, as each
> >> > PCI function in a slot is a separate DeviceState*. I need to
> >> > understand how multi-function PCI hotplug works, to find out what
> >> > we should do.
> >> 
> >> *Real* PCI devices consist of function 0 and optionally additional
> >> functions.  The device gets plugged as a whole (d'oh) into a slot with a
> >> specific device address.  Note that the dev.fn address addresses a
> >> *function*, not a device.
> >> 
> >> The way QEMU models this is "madness, yet there is method in't".  A PCI
> >> qdev models a PCI *function*, not a PCI device.  It gets plugged into a
> >> PCI qbus "slot" with a specific *function* address (dev.fn).  The
> >> functions that share the dev part of the function address together form
> >> a PCI device.  You have to plug something into function 0 for this to
> >> work.  Sufficiently weird function combinations might conceivably
> >> confuse software.  If I remember correctly, you have to hot-plug
> >> function 0 last (actually triggers the device hot-plug), and
> >> hot-unplugging function 0 yanks out all the functions.
> >> 
> >> When a device has just one function, which is a fairly common case, then
> >> you can pretend the PCI qdev is a PCI device.
> >> 
> >> So, what are the slots provided by a PCI (not PCIe) bus?  A *real* one
> >> provides 32 *device* slots.  A qbus, however, provides 256 *function*
> >> "slots".
> >> 
> >> How should query-device-slots report this?  All 256 function "slots", or
> >> just the 32 function 0 slots?  Since management software needs to know
> >> about the PCI qdev madness anyway, I'd say just the 32.
> >
> > So, if weach PCI function is a separate device for QEMU, isn't
> > this breaking your suggested rule about each slot supporting only
> > 1 device?
> 
> They all go into separate slots.  Evidence: each one needs its own
> addr=..., e.g. addr=6.0, addr=6.1, ...
> 
> The trouble is that the slots where function is non-zero aren't real,
> but an artifact of the weird way we model PCI.
> 
> >> Same question for PCIe, with and without ARI.
> >> 
> >> If we had had QOM back when we made this mess, we'd have designed PCI
> >> devices as a composition of PCI functions.  At least I hope we'd have.
> >> With such a design, we'd assemble functions into a device, then plug the
> >> device.  A slot would then be the same as for a real bus.
> >> 
> >> > I worry about having a general 1-slot = 1-device rule from the
> >> > beginning, because I don't know if it can be a problem when the
> >> > set of possible slots is too large. e.g.: should we return one
> >> > slot for each of the 256 possible devfns in a PCI bus? What if a
> >> > bus uses 32-bit identifiers for devices?
> >> >
> >> > My suggestion is to avoid any general claims about device limits
> >> > in slots in the documentation by now, until we have more
> >> > knowledge about other bus types.
> >> 
> >> For me, a slot can't take more than one device.  Something that can
> >> isn't a slot, it's a set of slots.
> >
> > So, is a PCI device number (that supports up to 8 functions, each
> > with its own device object) a slot, or a set of slots?
> 
> For real hardware, the device number identifies the slot.
> 
> In our weirdly modelled virtual world, we can pick between two
> alternatives for query-device-slots:
> 
> 1. Follow real hardware, and expose only real slots.  Management
>    applications supporting multi-function need to know that each real
>    slot is associated with a bunch of "unreal" slots, and that they need
>    to plug function 0 into the real slot (where it becomes the device),
>    and any other functions into associated unreal slots.
> 
>    Note that the .FN part in addr=DEV.FN is optional.  We could exploit
>    that and let query-device-slots pretend that the value of addr is a
>    number between 0 and 31.

Except that the "addr" parser will use the number as a raw devfn
value if it's not a string.

This seems to mean "-device ...,addr=9" will result on
slot=8,function=0 but "{ 'command': 'device_add', ... 'addr':8
... }" will result on slot=1,function=1. I didn't test that yet.

But this problem should be addressed by the new
"device-number"[1] and "function" properties, already.

[1] I have tried to use "slot" but it conflicts with an existing
    property on TYPE_PCIE_SLOT.

> 
> 2. Screw real hardware, and expose real and unreal slots.  Management
>    applications need to know that unreal slots are only usable together
>    with their real slot.  Even when they only deal with single-function
>    devices.
> 
> Either way, management applications need to know more about our PCI bus
> type than about saner bus types.
> 
> A possible third alternative would be to create a "set of related slots
> that can only be used together in a certain way (which I can't explain
> to you)" abstraction, but I'm afraid that'll be basically the same as 1,
> only more complicated.
> 
> I'm leaning towards 1, because it's closer to real hardware, and I hope
> it requires slightly less ugliness in management applications.

I'm leaning towards 2 because it allows QEMU or management
software to automatically validate all arguments to every
device_add call with no extra information except the
query-device-slots data. Reporting info about the "function"
property might also be useful when handling ARI devices, but I'm
not sure yet.

My next RFC will probably implement (2) so we can see how it will
look like. But we should be able to easily change it back to (1)
with few lines of code.

> 
> >> Comitting to a design now that models this as a "slot" that can take
> >> multiple devices seems premature.  We really have no idea whether that
> >> would work!
> >> 
> >> But you made a good point: set of slots too large to permit
> >> query-device-slots enumerating its members may exist.
> >> 
> >> Enumerating members is just one way to describe a set.  An algorithm to
> >> generate the members is another.
> >> 
> >> Let's try for your hypothetical bus with 2^32 slots, each identified by
> >> an unsigned 32-bit number.  Say the syntax is bus=BUSNAME,addr=UINT32.
> >> We need to describe bus=BUSNAME,addr=UINT32.  The bus=BUSNAME part is
> >> simple enough: "props" contains a member "bus": "BUSNAME".  For the
> >> addr=UINT32 part, we'll have to invent suitable language to express "any
> >> integer between 0 and 2^32-1".
> >> 
> >> I believe this will be easier when the information for all slots is
> >> otherwise identical.  If it is, we can describe all 2^32 slots in one
> >> dictionary.  But if slot#7 and slot#42..666 are different, we need
> >> multiple, and probably a more complex language for slot addresses, to
> >> express things like "any integer between 0 and 2^32-1, except for 7 and
> >> 42..666".
> 
> A possible alternative is cascading: combine all entries covering a
> slot.
> 
> > The question for me is: do we need it now, or can we add this as
> > an extension in a future version?
> 
> I think we need to explore much of the problem space, then solve a
> useful part of the problem in a simple way.  The exploration hopefully
> enables us to design a simple solution that can actually grow into a
> more complete solution.

Understood.

I have the impression that changing existing output from
single-slots to slot-sets would be harder than starting with a
specification that support slots-sets since the beginning. I will
try to cook a proposal supporting slot-sets on the next RFC.

> 
> >> Note that including information on currently plugged devices makes slot
> >> information different.
> >> 
> >> Let's take a step back and consider what we want to achieve with
> >> query-device-slots.  I think the core objective is to let libvirt know:
> >> 
> >> * What slots exist
> >> 
> >> * What devices can be plugged into each slot
> >> 
> >> * Whether plugging / unplugging is possible right now
> >> 
> >> * What arguments need to be used to plug something into a specific slot
> >> 
> >> Your RFC design provides additional information, namely
> >> 
> >> * Currently plugged devices
> >> 
> >>   I'd expect this to be redundant with other queries, such as QOM
> >>   introspection.
> >> 
> >> * Whether hot-plug is possible
> >> 
> >>   If the machine has been started already, this is the same as whether
> >>   plugging / unplugging is possible right now.
> >> 
> >>   Else, it permits predicting whether it will be possible once the
> >>   machine is started.  Perhaps that's important to know, perhaps not.
> >> 
> >> * Perhaps (I'm not sure) even internal-only slots, i.e. ones that can
> >>   never be used with device_add.
> >> 
> >> Let's focus on the core objective and completely ignore the additional
> >> information for now.
> >> 
> >> >> > This looks like a trade-off between moving data to specific union
> >> >> > types (and representing type-specific limits/rules in the schema
> >> >> > documentation), or moving data to the base union type (and
> >> >> > representing type-specific limits as data in the base union
> >> >> > type).
> >> >> 
> >> >> Works for me.
> >> >> 
> >> >> Additionally, let's replace 'devices': [ 'str' ] by '*device': 'str',
> >> >> because a slot is either empty or has one device plugged in.
> >> >> 
> >> >> If we ever need to add a set-of-slots type, we'll have to extend the
> >> >> schema, but then we'll be in a much better position to know what we
> >> >> actually need.
> >> >> 
> >> >
> >> > I am working on a version that even removes 'devices', for
> >> > simplicity. Then we would have more freedom when extending the
> >> > schema later.
> >> 
> >> Yes, please.
> >> 
> >> >> >> >> > +#
> >> >> >> >> > +# @bus: ID of the bus object where the device can be plugged. Optional,
> >> >> >> >> > +#       as some devices don't need a bus to be plugged (e.g. CPUs).
> >> >> >> >> > +#       Can be copied to the "bus" argument to @device_add.
> >> >> >> >> 
> >> >> >> >> Suggest something like "Can be used as value for @device_add's bus
> >> >> >> >> option".
> >> >> >> >
> >> >> >> > Will do.
> >> >> >> >
> >> >> >> >> 
> >> >> >> >> Should we give similar information on the slot address?  The option name
> >> >> >> >> is obvious.  What about acceptable values?
> >> >> >> >
> >> >> >> > Actually, this part of the documentation was a leftover from a
> >> >> >> > previous version where @bus was inside DeviceSlotInfo. Now I
> >> >> >> > moved @bus inside @props for all device types, and it should be
> >> >> >> > documented the same way as the other fields inside @props.
> >> >> >> >
> >> >> >> >> 
> >> >> >> >> > +#
> >> >> >> >> > +# @type: type of device slot.
> >> >> >> >> > +#
> >> >> >> >> > +# @accepted-device-types: List of device types
> >> >> >> >> > accepted by the slot.  +#
> >> >> >> >> > Any device plugged to the slot should implement
> >> >> >> >> > +#                         one of the accepted
> >> >> >> >> > device types.  +# +# @max-devices: #optional
> >> >> >> >> > maximum number of devices that can be plugged +#
> >> >> >> >> > to the slot.
> >> >> >> >> 
> >> >> >> >> What does it mean when @max-devices isn't given?
> >> >> >> >
> >> >> >> > It means there is no hard limit on the number of
> >> >> >> > pluggable devices. sysbus is one of such cases.
> >> >> >> 
> >> >> >> Sysbus certainly has limits, but they're more
> >> >> >> complicated than just "at most @max-devices devices".
> >> >> >> 
> >> >> >> >> > +# +# @devices: List of QOM paths for devices
> >> >> >> >> > that are already plugged.  +# +# @available: If
> >> >> >> >> > false, the slot is not available for plugging any
> >> >> >> >> > device.  +#             This value can change at
> >> >> >> >> > runtime if condition changes +#             (e.g.
> >> >> >> >> > if the device becomes full, or if the machine +#
> >> >> >> >> > was already initialized and the slot doesn't
> >> >> >> >> > support +#             hotplug).  +# +#
> >> >> >> >> > @hotpluggable: If true, the slot accepts
> >> >> >> >> > hotplugged devices.  +# +# DeviceSlotInfo structs
> >> >> >> >> > always have a @props member, whose members +# can
> >> >> >> >> > be directly copied to the arguments to
> >> >> >> >> > @device_add.
> >> >> >> >> 
> >> >> >> >> Do you mean names of properties common to all
> >> >> >> >> @accepted-device-types?
> >> >> >> >
> >> >> >> > I mean that it should be safe to simply use the
> >> >> >> > keys+values in @props as arguments to device_add or
> >> >> >> > -device, for any slot @type.  Some clients may want
> >> >> >> > to extract some information from @props (if they
> >> >> >> > already manage PCI addresses, for example), but some
> >> >> >> > of them may simply choose any available slot and
> >> >> >> > copy @props blindly.
> >> >> >> >
> >> >> >> > I didn't want to state anything about QOM
> >> >> >> > properties, because I only want @props to represent
> >> >> >> > device_add/-device arguments. Some of those
> >> >> >> > arguments are consumed by the device_add code itself
> >> >> >> > (e.g. "bus") and others are translated to QOM
> >> >> >> > properties. I don't want the interface to impose any
> >> >> >> > restrictions on how this is implemented.
> >> >> >> 
> >> >> >> Hmm, is this meant to work as follows?  To plug a
> >> >> >> device into this slot, you use
> >> >> >> 
> >> >> >>     { "execute": "device_add", "arguments": {
> >> >> >>       "driver": TYPE, ... PROPS ... }
> >> >> >> 
> >> >> >> where TYPE is a (concrete subtype of a) member of
> >> >> >> @accepted-device-types, and PROPS is the value of
> >> >> >> @props spliced in.  Example: the data for slot 06.0 of
> >> >> >> PCI bus pci.0 would be something like
> >> >> >> 
> >> >> >>     { "bus": "pci.0",
> >> >> >
> >> >> > There's no "bus" field in DeviceSlotInfo. It was a
> >> >> > documentation bug.
> >> >> 
> >> >> Should read code, not docs ;)
> >> >> 
> >> >> >>       "props": { "bus": "pci.0", "addr": "06.0" } }
> >> >> >> 
> >> >> >> Doesn't match your example output; I guess I'm still
> >> >> >> misunderstanding something.
> >> >> >
> >> >> > This is a busy PCI slot, from my example:
> >> >> >
> >> >> >         { "available": false, "devices": [
> >> >> >             "/machine/q35/mch" ],
> >> >> >             "accepted-device-types": [
> >> >> >             "legacy-pci-device", "pci-express-device"
> >> >> >             ], "props": { "bus": "/machine/q35/pcie.0",
> >> >> 
> >> >> Does device_add device_add bus=/machine/q35/pcie.0 even
> >> >> work?  I know qbus_find() interprets /-separated paths,
> >> >> but their semantics are FUBAR (I can find details if
> >> >> you're curious).  We've long advised to use only values
> >> >> without '/', like bus=pcie.0.
> >> >
> >> > @device_add documentation says:
> >> >
> >> > # @bus: #optional the device's parent bus (device tree
> >> > path)
> >> 
> >> The line is a recent addition (commit 94cfd07).  Note
> >> "device tree path", not "QOM path".  I think it should
> >> either not mention paths at all, or advise people to stick
> >> to qdev IDs.
> >> 
> >> >> Now, QOM gives us a framework for interpreting paths
> >> >> sanely.  Switching the value of bus to QOM paths would be
> >> >> a compatibility break, though.  Matters only if the
> >> >> botched /-paths are actually used.
> >> >
> >> > I would be happy to use the bus name instead of the full
> >> > path, but I don't see any code that ensures that bus names
> >> > are really unique. Is there any existing mechanism to
> >> > ensure that?
> >> 
> >> Kind of.  qbus names are typically derived from the
> >> providing device's qdev ID, which is unique.  When they're
> >> not, clashes *are* possible.  We used to have one with -m
> >> isapc: the two IDE buses are provided by separate isa-ide
> >> qdevs, and both qbuses were named ide.0.  Fixed in commit
> >> 61de36.  Now the first one to register becomes ide.0, and
> >> the second one ide.1.  I'm not fond of such implicit naming,
> >> but we got bigger fish to fry.
> >> 
> >> Perhaps we should sidestep this mess and use QOM paths.
> >
> > Maybe, but I would like to make the returned information fit
> > what libvirt already uses in its internal address-space
> > allocation logic.
> >
> > If we return "pci.0", libvirt will understand what we are
> > talking about because they already have existing code that
> > assumes there's a bus named "pci.0" in some machine-types. If
> > we return a QOM path like "/machine/i440fx/pci.0", they can't
> > be sure we are really talking about the same thing (unless we
> > specify QOM path <=> bus name mapping rules very carefully).
> 
> I think the way to go here at least initially is to document
> that the value of bus is not to be interpreted, only passed to
> device_add as option "bus".
> 
> If ever gets changed to accept QOM paths, we can choose to let
> query-device-slots report them.

I was suggesting the opposite above: using the "pci.0" name to
help libvirt match the bus name with their existing logic that
chooses where to plug a device. In other words, letting them
interpret the value of @bus.

But never interpreting the value of @bus is a good idea. This
means any information libvirt needs when deciding on which bus to
plug a device should be available in the remaining PCIDeviceSlots
fields.

> 
> As long as we report qbus IDs, we should perhaps suppress slots that
> can't actually be addressed.  Example: say there's two qbuses named
> "ide.0" for some reason.  We then expose only the one you actually get
> with device_add bus=ide.0,...

I was assuming that a simple:
   if (qbus_find(bus->name) != bus) {
       continue;
   }
would be enough. But it seems to be more complex: qbus_find()
bus=ide.0 will use the first match if it is not full yet. But it
will skip it and look for another match in case it is full.

That means the slots on the second "ide.0" qbus might be
available, but only after the the first one is full. How should
we report the slots at the second bus?

I will look for a concrete example to illustrate that.

> 
> >> 
> >> >> >                 "addr": 0
> >> >> >             },
> >> >> >             "hotpluggable": false,
> >> >> >             "type": "pci",
> >> >> >             "max-devices": 1
> >> >> >         },
> >> [...]
diff mbox

Patch

diff --git a/qapi-schema.json b/qapi-schema.json
index d48ff3f..484d91e 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3166,6 +3166,120 @@ 
 { 'command': 'closefd', 'data': {'fdname': 'str'} }
 
 ##
+# @DeviceSlotType:
+#
+# Type of device slot
+#
+# @generic: Generic device slot, with no bus-specific information
+# @pci: PCI device slot
+# @cpu: CPU device slot
+##
+{ 'enum': 'DeviceSlotType',
+  'data': ['generic', 'pci', 'cpu'] }
+
+##
+# @DeviceSlotInfo:
+#
+# Information on a slot where devices can be plugged. Some buses
+# are represented as a single slot that can support multiple devices,
+# and some buses have multiple slots that are identified by arguments
+# to @device_add.
+#
+# @bus: ID of the bus object where the device can be plugged. Optional,
+#       as some devices don't need a bus to be plugged (e.g. CPUs).
+#       Can be copied to the "bus" argument to @device_add.
+#
+# @type: type of device slot.
+#
+# @accepted-device-types: List of device types accepted by the slot.
+#                         Any device plugged to the slot should implement
+#                         one of the accepted device types.
+#
+# @max-devices: #optional maximum number of devices that can be plugged
+#               to the slot.
+#
+# @devices: List of QOM paths for devices that are already plugged.
+#
+# @available: If false, the slot is not available for plugging any device.
+#             This value can change at runtime if condition changes
+#             (e.g. if the device becomes full, or if the machine
+#             was already initialized and the slot doesn't support
+#             hotplug).
+#
+# @hotpluggable: If true, the slot accepts hotplugged devices.
+#
+# DeviceSlotInfo structs always have a @props member, whose members
+# can be directly copied to the arguments to @device_add.
+##
+{ 'union': 'DeviceSlotInfo',
+  'base': { 'type': 'DeviceSlotType',
+            'accepted-device-types': [ 'str' ],
+            '*max-devices': 'int', 'devices': [ 'str' ],
+            'available': 'bool', 'hotpluggable': 'bool' },
+  'discriminator': 'type',
+  'data': { 'generic': 'GenericSlotInfo',
+            'pci': 'PCISlotInfo',
+            'cpu': 'CPUSlotInfo' } }
+
+##
+# @GenericSlotProperties:
+##
+{ 'struct': 'GenericSlotProperties',
+  'data': { 'bus': 'str' } }
+
+
+##
+# @GenericSlotInfo:
+#
+# Generic slot information, with no bus-specific information
+##
+{ 'struct': 'GenericSlotInfo',
+  'data': { 'props': 'GenericSlotProperties' } }
+
+##
+# @PCIDeviceSlotProperties:
+#
+# Properties that can be set when plugging a PCI device.
+#
+# @addr: "addr" argument to @device_add.
+#
+#FIXME: replace @addr with slot and function properties.
+##
+{ 'struct': 'PCIDeviceSlotProperties',
+  'data': { 'bus': 'str', 'addr': 'int' } }
+
+##
+# @PCISlotInfo:
+#
+# Information on a PCI device slot.
+#
+# @props: The @device_add arguments that can be used when plugging
+# the device.
+##
+{ 'struct': 'PCISlotInfo',
+  'data': { 'props': 'PCIDeviceSlotProperties' } }
+
+##
+# @CPUSlotInfo:
+#
+# Information on a CPU device slot.
+#
+# @props: The @device_add arguments that can be used when plugging
+# the device.
+##
+{ 'struct': 'CPUSlotInfo',
+  'data': { 'props': 'CpuInstanceProperties' } }
+
+##
+# @query-device-slots:
+#
+# Return the list of possible slots for plugging devices using
+# @device_add.
+##
+{ 'command': 'query-device-slots',
+  'returns': [ 'DeviceSlotInfo' ] }
+
+##
 # @MachineBusInfo
 #
 # Information about a bus present on a machine.
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index a7f9ac4..7a87234 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -214,6 +214,10 @@  struct BusClass {
      * but on some cases bus instances may override it.
      */
     const char *device_type;
+
+    /*TOD: write doc */
+    DeviceSlotInfoList *(*enumerate_slots)(BusState *bus, Error **errp);
+
 };
 
 typedef struct BusChild {
@@ -412,4 +416,6 @@  static inline bool qbus_is_hotpluggable(BusState *bus)
 void device_listener_register(DeviceListener *listener);
 void device_listener_unregister(DeviceListener *listener);
 
+bool qbus_is_full(BusState *bus);
+
 #endif
diff --git a/hw/core/bus.c b/hw/core/bus.c
index d2bf717..58b5c3d 100644
--- a/hw/core/bus.c
+++ b/hw/core/bus.c
@@ -21,6 +21,7 @@ 
 #include "qemu-common.h"
 #include "hw/qdev.h"
 #include "qapi/error.h"
+#include "qapi/clone-visitor.h"
 #include "qapi-visit.h"
 
 static void qbus_set_hotplug_handler_internal(BusState *bus, Object *handler,
@@ -225,12 +226,60 @@  static void bus_get_device_type(Object *obj, Visitor *v,
     visit_type_strList(v, NULL, &bus->accepted_device_types, errp);
 }
 
+bool qbus_is_full(BusState *bus)
+{
+    BusClass *bus_class = BUS_GET_CLASS(bus);
+    return bus_class->max_dev && bus->max_index >= bus_class->max_dev;
+}
+
+/* Generic slot enumeration function: will return a single slot
+ * for the bus, with max_devices set to BusClass::max_dev, if set.
+ */
+static DeviceSlotInfoList *bus_generic_enumerate_slots(BusState *bus, Error **errp)
+{
+    Error *local_err = NULL;
+    BusClass *bc = BUS_GET_CLASS(bus);
+    strList **devlist;
+    BusChild *kid;
+    DeviceSlotInfoList *r = g_new0(DeviceSlotInfoList, 1);
+
+    r->value = g_new0(DeviceSlotInfo, 1);
+    r->value->type = DEVICE_SLOT_TYPE_GENERIC;
+    r->value->has_max_devices = bc->max_dev > 0;
+    r->value->max_devices = bc->max_dev;
+    r->value->accepted_device_types = QAPI_CLONE(strList, bus->accepted_device_types);
+    r->value->u.generic.props = g_new0(GenericSlotProperties, 1);
+    r->value->u.generic.props->bus = object_get_canonical_path(OBJECT(bus));
+
+    devlist = &r->value->devices;
+    QTAILQ_FOREACH(kid, &bus->children, sibling) {
+        DeviceState *dev = kid->child;
+        strList *item = g_new0(strList, 1);
+        item->value = object_get_canonical_path(OBJECT(dev));
+        *devlist = item;
+        devlist = &item->next;
+    }
+    r->value->hotpluggable = qbus_is_hotpluggable(bus);
+
+    /* Conditions that make a bus unavailable:
+     * - Bus already full
+     * - Hotplug when the bus is not hotpluggable
+     */
+    r->value->available =
+        !(qbus_is_full(bus) ||
+          (qdev_hotplug && !qbus_is_hotpluggable(bus)));
+
+    error_propagate(errp, local_err);
+    return r;
+}
+
 static void bus_class_init(ObjectClass *class, void *data)
 {
     BusClass *bc = BUS_CLASS(class);
 
     class->unparent = bus_unparent;
     bc->get_fw_dev_path = default_bus_get_fw_dev_path;
+    bc->enumerate_slots = bus_generic_enumerate_slots;
 
     object_class_property_add(class, "accepted-device-types", "strList",
                               bus_get_device_type, NULL, NULL, NULL,
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 2eac71a..9bcc116 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -41,6 +41,7 @@ 
 #include "hw/hotplug.h"
 #include "hw/boards.h"
 #include "qemu/cutils.h"
+#include "qapi/clone-visitor.h"
 
 //#define DEBUG_PCI
 #ifdef DEBUG_PCI
@@ -141,6 +142,8 @@  static uint16_t pcibus_numa_node(PCIBus *bus)
     return NUMA_NODE_UNASSIGNED;
 }
 
+static DeviceSlotInfoList *pci_bus_enumerate_slots(BusState *bus, Error **errp);
+
 static void pci_bus_class_init(ObjectClass *klass, void *data)
 {
     BusClass *k = BUS_CLASS(klass);
@@ -156,6 +159,7 @@  static void pci_bus_class_init(ObjectClass *klass, void *data)
      * but overrides BusClass::device_type to INTERFACE_PCIE_DEVICE
      */
     k->device_type = INTERFACE_LEGACY_PCI_DEVICE;
+    k->enumerate_slots = pci_bus_enumerate_slots;
 
     pbc->is_root = pcibus_is_root;
     pbc->bus_num = pcibus_num;
@@ -967,6 +971,81 @@  uint16_t pci_requester_id(PCIDevice *dev)
     return pci_req_id_cache_extract(&dev->requester_id_cache);
 }
 
+static bool pci_bus_has_pcie_upstream_port(PCIBus *bus)
+{
+    PCIDevice *parent_dev = pci_bridge_get_device(bus);
+
+    /* Device associated with an upstream port.
+     * As there are several types of these, it's easier to check the
+     * parent device: upstream ports are always connected to
+     * root or downstream ports.
+     */
+    return parent_dev &&
+        pci_is_express(parent_dev) &&
+        parent_dev->exp.exp_cap &&
+        (pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_ROOT_PORT ||
+         pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_DOWNSTREAM);
+}
+
+static PCIDevice *pci_bus_get_function_0(PCIBus *bus, int devfn)
+{
+    if(pci_bus_has_pcie_upstream_port(bus)) {
+        /* With an upstream PCIe port, we only support 1 device at slot 0 */
+        return bus->devices[0];
+    } else {
+        /* Other bus types might support multiple devices at slots 0-31 */
+        return bus->devices[PCI_DEVFN(PCI_SLOT(devfn), 0)];
+    }
+}
+
+PCIDevice *pci_get_function_0(PCIDevice *pci_dev)
+{
+    return pci_bus_get_function_0(pci_dev->bus, pci_dev->devfn);
+}
+
+static DeviceSlotInfoList *pci_bus_enumerate_slots(BusState *bus, Error **errp)
+{
+    PCIBus *pb = PCI_BUS(bus);
+    int devfn;
+    DeviceSlotInfoList *r = NULL;
+    DeviceSlotInfoList **next = &r;
+
+    for(devfn = pb->devfn_min; devfn < ARRAY_SIZE(pb->devices);
+        devfn += PCI_FUNC_MAX) {
+        DeviceSlotInfoList *i = g_new0(DeviceSlotInfoList, 1);
+        i->value = g_new0(DeviceSlotInfo, 1);
+        i->value->type = DEVICE_SLOT_TYPE_PCI;
+        i->value->has_max_devices = true;
+        i->value->max_devices = 1;
+        i->value->u.pci.props = g_new0(PCIDeviceSlotProperties, 1);
+        i->value->u.pci.props->bus = object_get_canonical_path(OBJECT(bus));
+        i->value->u.pci.props->addr = devfn;
+        /*TODO: add info about accepting only bridges on extra PCI root buses */
+        i->value->accepted_device_types = QAPI_CLONE(strList, bus->accepted_device_types);
+        if (pb->devices[devfn]) {
+            i->value->devices = g_new0(strList, 1);
+            i->value->devices->value =
+                object_get_canonical_path(OBJECT(pb->devices[devfn]));
+        }
+
+        i->value->hotpluggable = qbus_is_hotpluggable(bus);
+        /* Conditions that make a devfn unavailable:
+         * - Hotplug when function 0 is already occupied
+         * - devfn already occupied by a device
+         * - Hotplug when the bus is not hotpluggable
+         */
+        i->value->available =
+            !((qdev_hotplug && pci_bus_get_function_0(pb, devfn)) ||
+              (pb->devices[devfn]) ||
+              (qdev_hotplug && !qbus_is_hotpluggable(bus)));
+
+        *next = i;
+        next = &i->next;
+    }
+
+    return r;
+}
+
 /* -1 for devfn means auto assign */
 static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
                                          const char *name, int devfn,
@@ -2607,33 +2686,6 @@  void pci_bus_get_w64_range(PCIBus *bus, Range *range)
     pci_for_each_device_under_bus(bus, pci_dev_get_w64, range);
 }
 
-static bool pcie_has_upstream_port(PCIDevice *dev)
-{
-    PCIDevice *parent_dev = pci_bridge_get_device(dev->bus);
-
-    /* Device associated with an upstream port.
-     * As there are several types of these, it's easier to check the
-     * parent device: upstream ports are always connected to
-     * root or downstream ports.
-     */
-    return parent_dev &&
-        pci_is_express(parent_dev) &&
-        parent_dev->exp.exp_cap &&
-        (pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_ROOT_PORT ||
-         pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_DOWNSTREAM);
-}
-
-PCIDevice *pci_get_function_0(PCIDevice *pci_dev)
-{
-    if(pcie_has_upstream_port(pci_dev)) {
-        /* With an upstream PCIe port, we only support 1 device at slot 0 */
-        return pci_dev->bus->devices[0];
-    } else {
-        /* Other bus types might support multiple devices at slots 0-31 */
-        return pci_dev->bus->devices[PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 0)];
-    }
-}
-
 MSIMessage pci_get_msi_message(PCIDevice *dev, int vector)
 {
     MSIMessage msg;
diff --git a/qdev-monitor.c b/qdev-monitor.c
index c73410c..b4071c4 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -29,6 +29,8 @@ 
 #include "qemu/error-report.h"
 #include "qemu/help_option.h"
 #include "sysemu/block-backend.h"
+#include "qapi/clone-visitor.h"
+#include "hw/boards.h"
 
 /*
  * Aliases were a bad idea from the start.  Let's keep them
@@ -399,12 +401,6 @@  static DeviceState *qbus_find_dev(BusState *bus, char *elem)
     return NULL;
 }
 
-static inline bool qbus_is_full(BusState *bus)
-{
-    BusClass *bus_class = BUS_GET_CLASS(bus);
-    return bus_class->max_dev && bus->max_index >= bus_class->max_dev;
-}
-
 /*
  * Search the tree rooted at @bus for a bus.
  * If @name, search for a bus with that name.  Note that bus names
@@ -631,6 +627,84 @@  DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
     return dev;
 }
 
+typedef struct SlotListState {
+    DeviceSlotInfoList *result;
+    DeviceSlotInfoList **next;
+    Error *err;
+} SlotListState;
+
+static int walk_bus(Object *obj, void *opaque)
+{
+    SlotListState *s = opaque;
+    if (object_dynamic_cast(obj, TYPE_BUS)) {
+        BusState *bus = BUS(obj);
+        BusClass *bc = BUS_GET_CLASS(bus);
+        DeviceSlotInfoList *l = bc->enumerate_slots(bus, &s->err);
+        *s->next = l;
+        for (; l; l = l->next) {
+            s->next = &l->next;
+        }
+        if (s->err) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+DeviceSlotInfoList *qmp_query_device_slots(Error **errp)
+{
+    SlotListState s = { };
+    MachineState *ms = MACHINE(qdev_get_machine());
+    MachineClass *mc = MACHINE_GET_CLASS(ms);
+
+    s.next = &s.result;
+
+    /* We build the device slot list from two sources:
+     * 1) Calling the BusClass::enumerate_slots() method on all buses;
+     * 2) The return value of MachineClass::query_hotpluggable_cpus()
+     */
+
+
+    object_child_foreach_recursive(qdev_get_machine(), walk_bus, &s);
+    if (s.err) {
+        goto out;
+    }
+
+    if (mc->query_hotpluggable_cpus) {
+        HotpluggableCPUList *hcl = mc->query_hotpluggable_cpus(ms);
+        HotpluggableCPUList *i;
+
+        for (i = hcl; i; i = i->next) {
+            DeviceSlotInfoList *r = g_new0(DeviceSlotInfoList, 1);
+            HotpluggableCPU *hc = i->value;
+            r->value = g_new0(DeviceSlotInfo, 1);
+            r->value->type = DEVICE_SLOT_TYPE_CPU;
+            r->value->accepted_device_types = g_new0(strList, 1);
+            r->value->accepted_device_types->value = g_strdup(hc->type);
+            r->value->has_max_devices = true;
+            r->value->max_devices = 1;
+            r->value->available = !hc->has_qom_path;
+            /*TODO: should it be always true? */
+            r->value->hotpluggable = true;
+
+            if (hc->has_qom_path) {
+                r->value->devices = g_new0(strList, 1);
+                r->value->devices->value = g_strdup(hc->qom_path);
+            }
+            r->value->u.cpu.props = QAPI_CLONE(CpuInstanceProperties,
+                                               hc->props);
+            *s.next = r;
+            s.next = & r->next;
+        }
+
+        qapi_free_HotpluggableCPUList(hcl);
+    }
+
+out:
+    error_propagate(errp, s.err);
+    return s.result;
+}
+
 
 #define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
 static void qbus_print(Monitor *mon, BusState *bus, int indent);