[libvirt] [PATCH v4 00/14] Basic implementation of persistent reservations

v4 of: https://www.redhat.com/archives/libvir-list/2018-March/msg00745.html diff to v3: - Peter's review worked in Michal Privoznik (14): virstoragefile: Introduce virStoragePRDef qemuDomainDiskChangeSupported: Deny changing reservations qemu: Introduce pr-manager-helper capability qemu: Generate alias and socket path for pr-helper qemu: Store pr runtime data in status XML qemu_ns: Allow /dev/mapper/control for PR qemu_cgroup: Allow /dev/mapper/control for PR qemu: Generate cmd line at startup qemu: Introduce pr_helper to qemu.conf qemu: Start PR daemon on domain startup qemu: Start PR daemon on disk hotplug qemu_hotplug: Hotplug of reservations qemu_hotplug: Hotunplug of reservations qemu: Detect pr-manager-helper capability docs/formatdomain.html.in | 25 ++- docs/schemas/domaincommon.rng | 34 +--- docs/schemas/storagecommon.rng | 50 +++++ m4/virt-driver-qemu.m4 | 5 + src/conf/domain_conf.c | 38 ++++ src/libvirt_private.syms | 6 + src/qemu/libvirtd_qemu.aug | 1 + src/qemu/qemu.conf | 4 + src/qemu/qemu_alias.c | 11 + src/qemu/qemu_alias.h | 2 + src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_cgroup.c | 33 ++- src/qemu/qemu_command.c | 94 +++++++++ src/qemu/qemu_command.h | 3 + src/qemu/qemu_conf.c | 7 +- src/qemu/qemu_conf.h | 1 + src/qemu/qemu_domain.c | 173 +++++++++++++++- src/qemu/qemu_domain.h | 10 + src/qemu/qemu_hotplug.c | 130 ++++++++++++ src/qemu/qemu_process.c | 224 +++++++++++++++++++++ src/qemu/qemu_process.h | 5 + src/qemu/test_libvirtd_qemu.aug.in | 1 + src/util/virdevmapper.c | 8 +- src/util/virstoragefile.c | 164 +++++++++++++++ src/util/virstoragefile.h | 18 ++ tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml | 1 + .../disk-virtio-scsi-reservations.args | 35 ++++ .../disk-virtio-scsi-reservations.xml | 49 +++++ tests/qemuxml2argvtest.c | 4 + .../disk-virtio-scsi-reservations.xml | 1 + tests/qemuxml2xmltest.c | 2 + 36 files changed, 1107 insertions(+), 39 deletions(-) create mode 100644 tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args create mode 100644 tests/qemuxml2argvdata/disk-virtio-scsi-reservations.xml create mode 120000 tests/qemuxml2xmloutdata/disk-virtio-scsi-reservations.xml -- 2.16.1

This is a definition that holds information on SCSI persistent reservation settings. The XML part looks like this: <reservations enabled='yes' managed='no'> <source type='unix' path='/path/to/qemu-pr-helper.sock' mode='client'/> </reservations> If @managed is set to 'yes' then the <source/> is not parsed. This design was agreed on here: https://www.redhat.com/archives/libvir-list/2017-November/msg01005.html Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/formatdomain.html.in | 25 +++- docs/schemas/domaincommon.rng | 34 +----- docs/schemas/storagecommon.rng | 50 ++++++++ src/conf/domain_conf.c | 38 ++++++ src/libvirt_private.syms | 3 + src/util/virstoragefile.c | 131 +++++++++++++++++++++ src/util/virstoragefile.h | 14 +++ .../disk-virtio-scsi-reservations.xml | 49 ++++++++ .../disk-virtio-scsi-reservations.xml | 1 + tests/qemuxml2xmltest.c | 2 + 10 files changed, 316 insertions(+), 31 deletions(-) create mode 100644 tests/qemuxml2argvdata/disk-virtio-scsi-reservations.xml create mode 120000 tests/qemuxml2xmloutdata/disk-virtio-scsi-reservations.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 5e99884dc5..c20e98b4ef 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2563,7 +2563,10 @@ </disk> <disk type='block' device='lun'> <driver name='qemu' type='raw'/> - <source dev='/dev/sda'/> + <source dev='/dev/sda'> + <reservations enabled='yes' managed='no'> + <source type='unix' path='/path/to/qemu-pr-helper' mode='client'/> + </reservations> <target dev='sda' bus='scsi'/> <address type='drive' controller='0' bus='0' target='3' unit='0'/> </disk> @@ -2926,6 +2929,26 @@ <a href="formatstorageencryption.html">Storage Encryption</a> page for more information. </dd> + <dt><code>reservations</code></dt> + <dd><span class="since">Since libvirt 4.1.0</span>, the + <code>reservations</code> can be a sub-element of the + <code>source</code> element for storage sources (QEMU driver only). + If present (and enabled) it enabled persistent reservations for + SCSI based disks. The element has one mandatory attribute + <code>enabled</code> with accepted values <code>yes</code> and + <code>no</code>. If the feature is enabled, then there's another + mandatory attribute <code>managed</code> (accepted values are the + same as for <code>enabled</code>) that enables or disables libvirt + spawning any helper process (should one be needed). However, if + libvirt is not enabled spawning helper process (i.e. hypervisor + should just use one which is already running), path to its socket + must be provided in child element <code>source</code>, which + currently accepts only the following attributes: <code>type</code> + with one value <code>unix</code>, <code>path</code> with path the + socket, and finally <code>mode</code> which accepts either + <code>server</code> or <code>client</code> and specifies the role + of hypervisor. + </dd> </dl> <p> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 4cab55f05d..93084887fb 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -1530,6 +1530,9 @@ <optional> <ref name="encryption"/> </optional> + <optional> + <ref name="reservations"/> + </optional> <zeroOrMore> <ref name='devSeclabel'/> </zeroOrMore> @@ -2434,18 +2437,6 @@ </attribute> </optional> </define> - <define name="reconnect"> - <element name="reconnect"> - <attribute name="enabled"> - <ref name="virYesNo"/> - </attribute> - <optional> - <attribute name="timeout"> - <ref name="unsignedInt"/> - </attribute> - </optional> - </element> - </define> <!-- An interface description can either be of type bridge in which case @@ -2494,24 +2485,7 @@ <value>vhostuser</value> </attribute> <interleave> - <element name="source"> - <attribute name="type"> - <value>unix</value> - </attribute> - <attribute name="path"> - <ref name="absFilePath"/> - </attribute> - <attribute name="mode"> - <choice> - <value>server</value> - <value>client</value> - </choice> - </attribute> - <optional> - <ref name="reconnect"/> - </optional> - <empty/> - </element> + <ref name="unixSocketSource"/> <ref name="interface-options"/> </interleave> </group> diff --git a/docs/schemas/storagecommon.rng b/docs/schemas/storagecommon.rng index edee1b0845..eed0b33347 100644 --- a/docs/schemas/storagecommon.rng +++ b/docs/schemas/storagecommon.rng @@ -39,6 +39,56 @@ </element> </define> + <define name="reconnect"> + <element name="reconnect"> + <attribute name="enabled"> + <ref name="virYesNo"/> + </attribute> + <optional> + <attribute name="timeout"> + <ref name="unsignedInt"/> + </attribute> + </optional> + </element> + </define> + + <define name='unixSocketSource'> + <element name="source"> + <attribute name="type"> + <value>unix</value> + </attribute> + <attribute name="path"> + <ref name="absFilePath"/> + </attribute> + <attribute name="mode"> + <choice> + <value>server</value> + <value>client</value> + </choice> + </attribute> + <optional> + <ref name="reconnect"/> + </optional> + <empty/> + </element> + </define> + + <define name='reservations'> + <element name='reservations'> + <attribute name='enabled'> + <ref name='virYesNo'/> + </attribute> + <optional> + <attribute name='managed'> + <ref name='virYesNo'/> + </attribute> + </optional> + <optional> + <ref name='unixSocketSource'/> + </optional> + </element> + </define> + <define name='secret'> <element name='secret'> <attribute name='type'> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index d23182f18a..5943d05e0e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5210,6 +5210,13 @@ virDomainDiskDefValidate(const virDomainDiskDef *disk) } } + if (disk->src->pr && + disk->device != VIR_DOMAIN_DISK_DEVICE_LUN) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("<reservations/> allowed only for lun disks")); + return -1; + } + /* Reject disks with a bus type that is not compatible with the * given address type. The function considers only buses that are * handled in common code. For other bus types it's not possible @@ -8602,6 +8609,31 @@ virDomainDiskSourcePrivateDataParse(xmlNodePtr node, } +static int +virDomainDiskSourcePRParse(xmlNodePtr node, + xmlXPathContextPtr ctxt, + virStoragePRDefPtr *pr) +{ + xmlNodePtr saveNode = ctxt->node; + int ret = -1; + + ctxt->node = node; + + if (!(ctxt->node = virXPathNode("./reservations", ctxt))) { + ret = 0; + goto cleanup; + } + + if (!(*pr = virStoragePRDefParseXML(ctxt))) + goto cleanup; + + ret = 0; + cleanup: + ctxt->node = saveNode; + return ret; +} + + static int virDomainStorageSourceParse(xmlNodePtr node, xmlXPathContextPtr ctxt, @@ -8648,6 +8680,9 @@ virDomainStorageSourceParse(xmlNodePtr node, !(src->encryption = virStorageEncryptionParseNode(tmp, ctxt))) goto cleanup; + if (virDomainDiskSourcePRParse(node, ctxt, &src->pr) < 0) + goto cleanup; + if (virSecurityDeviceLabelDefParseXML(&src->seclabels, &src->nseclabels, ctxt, flags) < 0) goto cleanup; @@ -22927,6 +22962,9 @@ virDomainStorageSourceFormat(virBufferPtr attrBuf, virStorageEncryptionFormat(childBuf, src->encryption) < 0) return -1; + if (src->pr) + virStoragePRDefFormat(childBuf, src->pr); + return 0; } diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index cab324c4d7..b39b694c60 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2797,6 +2797,9 @@ virStorageNetHostDefFree; virStorageNetHostTransportTypeFromString; virStorageNetHostTransportTypeToString; virStorageNetProtocolTypeToString; +virStoragePRDefFormat; +virStoragePRDefFree; +virStoragePRDefParseXML; virStorageSourceBackingStoreClear; virStorageSourceClear; virStorageSourceCopy; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 67b9ec71ac..9917837513 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1892,6 +1892,136 @@ virStorageAuthDefFormat(virBufferPtr buf, } +void +virStoragePRDefFree(virStoragePRDefPtr prd) +{ + if (!prd) + return; + + VIR_FREE(prd->path); + VIR_FREE(prd); +} + + +virStoragePRDefPtr +virStoragePRDefParseXML(xmlXPathContextPtr ctxt) +{ + virStoragePRDefPtr prd, ret = NULL; + char *enabled = NULL; + char *managed = NULL; + char *type = NULL; + char *path = NULL; + char *mode = NULL; + + if (VIR_ALLOC(prd) < 0) + return NULL; + + if (!(enabled = virXPathString("string(./@enabled)", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing @enabled attribute for <reservations/>")); + goto cleanup; + } + + if ((prd->enabled = virTristateBoolTypeFromString(enabled)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid value for 'enabled': %s"), enabled); + goto cleanup; + } + + if (prd->enabled == VIR_TRISTATE_BOOL_YES) { + if (!(managed = virXPathString("string(./@managed)", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing @managed attribute for <reservations/>")); + goto cleanup; + } + + if ((prd->managed = virTristateBoolTypeFromString(managed)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid value for 'managed': %s"), managed); + goto cleanup; + } + + if (prd->managed == VIR_TRISTATE_BOOL_NO) { + type = virXPathString("string(./source[1]/@type)", ctxt); + path = virXPathString("string(./source[1]/@path)", ctxt); + mode = virXPathString("string(./source[1]/@mode)", ctxt); + + if (!type) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing connection type for <reservations/>")); + goto cleanup; + } + + if (!path) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing path for <reservations/>")); + goto cleanup; + } + + if (!mode) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing connection mode for <reservations/>")); + goto cleanup; + } + + if (STRNEQ(type, "unix")) { + virReportError(VIR_ERR_XML_ERROR, + _("unsupported connection type for <reservations/>: %s"), + type); + goto cleanup; + } + + if (STRNEQ(mode, "client")) { + virReportError(VIR_ERR_XML_ERROR, + _("unsupported connection mode for <reservations/>: %s"), + mode); + goto cleanup; + } + + VIR_STEAL_PTR(prd->path, path); + } + } + + ret = prd; + prd = NULL; + + cleanup: + VIR_FREE(mode); + VIR_FREE(path); + VIR_FREE(type); + VIR_FREE(managed); + VIR_FREE(enabled); + virStoragePRDefFree(prd); + return ret; +} + + +void +virStoragePRDefFormat(virBufferPtr buf, + virStoragePRDefPtr prd) +{ + virBufferAsprintf(buf, "<reservations enabled='%s'", + virTristateBoolTypeToString(prd->enabled)); + if (prd->enabled == VIR_TRISTATE_BOOL_YES) { + virBufferAsprintf(buf, " managed='%s'", + virTristateBoolTypeToString(prd->managed)); + if (prd->managed == VIR_TRISTATE_BOOL_NO) { + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 2); + virBufferAddLit(buf, "<source type='unix'"); + virBufferEscapeString(buf, " path='%s'", prd->path); + virBufferAddLit(buf, " mode='client'/>\n"); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</reservations>\n"); + } else { + virBufferAddLit(buf, "/>\n"); + } + } else { + virBufferAddLit(buf, "/>\n"); + } +} + + virSecurityDeviceLabelDefPtr virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src, const char *model) @@ -2270,6 +2400,7 @@ virStorageSourceClear(virStorageSourcePtr def) virBitmapFree(def->features); VIR_FREE(def->compat); virStorageEncryptionFree(def->encryption); + virStoragePRDefFree(def->pr); virStorageSourceSeclabelsClear(def); virStoragePermsFree(def->perms); VIR_FREE(def->timestamps); diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 596746ccb7..b705ab1f92 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -216,6 +216,14 @@ struct _virStorageAuthDef { virSecretLookupTypeDef seclookupdef; }; +typedef struct _virStoragePRDef virStoragePRDef; +typedef virStoragePRDef *virStoragePRDefPtr; +struct _virStoragePRDef { + int enabled; /* enum virTristateBool */ + int managed; /* enum virTristateBool */ + char *path; +}; + typedef struct _virStorageDriverData virStorageDriverData; typedef virStorageDriverData *virStorageDriverDataPtr; @@ -243,6 +251,7 @@ struct _virStorageSource { bool authInherited; virStorageEncryptionPtr encryption; bool encryptionInherited; + virStoragePRDefPtr pr; virObjectPtr privateData; @@ -370,6 +379,11 @@ virStorageAuthDefPtr virStorageAuthDefParse(xmlNodePtr node, xmlXPathContextPtr ctxt); void virStorageAuthDefFormat(virBufferPtr buf, virStorageAuthDefPtr authdef); +void virStoragePRDefFree(virStoragePRDefPtr prd); +virStoragePRDefPtr virStoragePRDefParseXML(xmlXPathContextPtr ctxt); +void virStoragePRDefFormat(virBufferPtr buf, + virStoragePRDefPtr prd); + virSecurityDeviceLabelDefPtr virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src, const char *model); diff --git a/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.xml b/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.xml new file mode 100644 index 0000000000..036c6e3c25 --- /dev/null +++ b/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.xml @@ -0,0 +1,49 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>8</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-i686</emulator> + <disk type='block' device='lun'> + <driver name='qemu' type='raw'/> + <source dev='/dev/HostVG/QEMUGuest1'> + <reservations enabled='yes' managed='yes'/> + </source> + <target dev='sda' bus='scsi'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <disk type='block' device='lun'> + <driver name='qemu' type='raw'/> + <source dev='/dev/HostVG/QEMUGuest2'> + <reservations enabled='yes' managed='no'> + <source type='unix' path='/path/to/qemu-pr-helper.sock' mode='client'/> + </reservations> + </source> + <target dev='sdb' bus='scsi'/> + <address type='drive' controller='0' bus='0' target='0' unit='1'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='scsi' index='0' model='virtio-scsi'> + <driver queues='8'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/disk-virtio-scsi-reservations.xml b/tests/qemuxml2xmloutdata/disk-virtio-scsi-reservations.xml new file mode 120000 index 0000000000..dde52fd1db --- /dev/null +++ b/tests/qemuxml2xmloutdata/disk-virtio-scsi-reservations.xml @@ -0,0 +1 @@ +../qemuxml2argvdata/disk-virtio-scsi-reservations.xml \ No newline at end of file diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index d123180e79..4c069497aa 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -387,6 +387,8 @@ mymain(void) QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_VIRTIO_SCSI); DO_TEST("disk-virtio-scsi-num_queues", QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_VIRTIO_SCSI); + DO_TEST("disk-virtio-scsi-reservations", + QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_VIRTIO_SCSI); DO_TEST("disk-virtio-scsi-cmd_per_lun", QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_VIRTIO_SCSI); DO_TEST("disk-virtio-scsi-max_sectors", -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
This is a definition that holds information on SCSI persistent reservation settings. The XML part looks like this:
<reservations enabled='yes' managed='no'> <source type='unix' path='/path/to/qemu-pr-helper.sock' mode='client'/> </reservations>
If @managed is set to 'yes' then the <source/> is not parsed. This design was agreed on here:
https://www.redhat.com/archives/libvir-list/2017-November/msg01005.html
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/formatdomain.html.in | 25 +++- docs/schemas/domaincommon.rng | 34 +----- docs/schemas/storagecommon.rng | 50 ++++++++ src/conf/domain_conf.c | 38 ++++++ src/libvirt_private.syms | 3 + src/util/virstoragefile.c | 131 +++++++++++++++++++++ src/util/virstoragefile.h | 14 +++ .../disk-virtio-scsi-reservations.xml | 49 ++++++++ .../disk-virtio-scsi-reservations.xml | 1 + tests/qemuxml2xmltest.c | 2 + 10 files changed, 316 insertions(+), 31 deletions(-) create mode 100644 tests/qemuxml2argvdata/disk-virtio-scsi-reservations.xml create mode 120000 tests/qemuxml2xmloutdata/disk-virtio-scsi-reservations.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 5e99884dc5..c20e98b4ef 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2563,7 +2563,10 @@ </disk> <disk type='block' device='lun'> <driver name='qemu' type='raw'/> - <source dev='/dev/sda'/> + <source dev='/dev/sda'> + <reservations enabled='yes' managed='no'> + <source type='unix' path='/path/to/qemu-pr-helper' mode='client'/> + </reservations> <target dev='sda' bus='scsi'/> <address type='drive' controller='0' bus='0' target='3' unit='0'/> </disk> @@ -2926,6 +2929,26 @@ <a href="formatstorageencryption.html">Storage Encryption</a> page for more information. </dd> + <dt><code>reservations</code></dt> + <dd><span class="since">Since libvirt 4.1.0</span>, the
Now at least 4.3
+ <code>reservations</code> can be a sub-element of the + <code>source</code> element for storage sources (QEMU driver only). + If present (and enabled) it enabled persistent reservations for
s/enabled/enables
+ SCSI based disks. The element has one mandatory attribute + <code>enabled</code> with accepted values <code>yes</code> and + <code>no</code>. If the feature is enabled, then there's another
<unrelatedRant>I wish we were consistent about using <code> around 'yes' and 'no' - some places use "yes" and/or "no".</unrelatedRant>
+ mandatory attribute <code>managed</code> (accepted values are the + same as for <code>enabled</code>) that enables or disables libvirt + spawning any helper process (should one be needed). However, if
s/any/a/ ? s/(should one be needed)// IOW: What really decides if one is needed?
+ libvirt is not enabled spawning helper process (i.e. hypervisor
Starting from "However, if.. " This reads strange - why not just indicate "If enabled is yes, then libvirt will ...; otherwise, libvirt will .... Taken from patch 4: + /* Managed PR means there's one pr-manager object per domain + * and the pr-helper process is spawned and managed by + * libvirt. + * If PR is unmanaged there's one pr-manager object per disk + * (in general each disk can have its own pr-manager) and + * it's user's responsibility to start the pr-helper process. + */ When the PR is unmanaged, then the path to its socket...
+ should just use one which is already running), path to its socket + must be provided in child element <code>source</code>, which + currently accepts only the following attributes: <code>type</code> + with one value <code>unix</code>, <code>path</code> with path the + socket, and finally <code>mode</code> which accepts either + <code>server</code> or <code>client</code> and specifies the role + of hypervisor.
mode only Parse's and Format's "client"... Perhaps the best thing to indicate here is that since libvirt is then a client of the user provided pr-helper process and will use a unix socket to connect to the process, the type must be unix and the mode must be client with the path being the path to the socket which libvirt will attempt to connect to.
+ </dd> </dl>
<p>
[...]
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index d23182f18a..5943d05e0e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5210,6 +5210,13 @@ virDomainDiskDefValidate(const virDomainDiskDef *disk) } }
+ if (disk->src->pr && + disk->device != VIR_DOMAIN_DISK_DEVICE_LUN) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("<reservations/> allowed only for lun disks"));
allowed or required ;-) s/disks/devices/
+ return -1; + } + /* Reject disks with a bus type that is not compatible with the * given address type. The function considers only buses that are * handled in common code. For other bus types it's not possible
[...]
static int virDomainStorageSourceParse(xmlNodePtr node, xmlXPathContextPtr ctxt, @@ -8648,6 +8680,9 @@ virDomainStorageSourceParse(xmlNodePtr node, !(src->encryption = virStorageEncryptionParseNode(tmp, ctxt))) goto cleanup;
+ if (virDomainDiskSourcePRParse(node, ctxt, &src->pr) < 0) + goto cleanup; + if (virSecurityDeviceLabelDefParseXML(&src->seclabels, &src->nseclabels, ctxt, flags) < 0) goto cleanup; @@ -22927,6 +22962,9 @@ virDomainStorageSourceFormat(virBufferPtr attrBuf, virStorageEncryptionFormat(childBuf, src->encryption) < 0) return -1;
+ if (src->pr) + virStoragePRDefFormat(childBuf, src->pr);
Or just have virStoragePRDefFormat return if passed src->pr == NULL, IDC either way.
+ return 0; }
[...] With at least the formatdomain and lun device error message adjustment, Reviewed-by: John Ferlan <jferlan@redhat.com> John

Couple of reasons for that: a) there's no monitor command to change path where the pr-helper connects to, or b) there's no monitor command to introduce a new pr-helper for a disk that already exists. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt_private.syms | 1 + src/qemu/qemu_domain.c | 8 ++++++++ src/util/virstoragefile.c | 18 ++++++++++++++++++ src/util/virstoragefile.h | 2 ++ 4 files changed, 29 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b39b694c60..a376e3bb0d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2799,6 +2799,7 @@ virStorageNetHostTransportTypeToString; virStorageNetProtocolTypeToString; virStoragePRDefFormat; virStoragePRDefFree; +virStoragePRDefIsEqual; virStoragePRDefParseXML; virStorageSourceBackingStoreClear; virStorageSourceClear; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 100304fd05..5a7b5f8417 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -7820,6 +7820,14 @@ qemuDomainDiskChangeSupported(virDomainDiskDefPtr disk, CHECK_EQ(src->readonly, "readonly", true); CHECK_EQ(src->shared, "shared", true); + if (!virStoragePRDefIsEqual(disk->src->pr, + orig_disk->src->pr)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("cannot modify field '%s' of the disk"), + "reservations"); + return false; + } + #undef CHECK_EQ return true; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 9917837513..b017024b2f 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -2022,6 +2022,24 @@ virStoragePRDefFormat(virBufferPtr buf, } +bool +virStoragePRDefIsEqual(virStoragePRDefPtr a, + virStoragePRDefPtr b) +{ + if (!a && !b) + return true; + + if (!a || !b) + return false; + + if (a->enabled != b->enabled || + a->managed != b->managed || + STRNEQ_NULLABLE(a->path, b->path)) + return false; + + return true; +} + virSecurityDeviceLabelDefPtr virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src, const char *model) diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index b705ab1f92..77853eefe8 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -383,6 +383,8 @@ void virStoragePRDefFree(virStoragePRDefPtr prd); virStoragePRDefPtr virStoragePRDefParseXML(xmlXPathContextPtr ctxt); void virStoragePRDefFormat(virBufferPtr buf, virStoragePRDefPtr prd); +bool virStoragePRDefIsEqual(virStoragePRDefPtr a, + virStoragePRDefPtr b); virSecurityDeviceLabelDefPtr virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src, -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Couple of reasons for that:
a) there's no monitor command to change path where the pr-helper connects to, or b) there's no monitor command to introduce a new pr-helper for a disk that already exists.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt_private.syms | 1 + src/qemu/qemu_domain.c | 8 ++++++++ src/util/virstoragefile.c | 18 ++++++++++++++++++ src/util/virstoragefile.h | 2 ++ 4 files changed, 29 insertions(+)
[...]
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 9917837513..b017024b2f 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -2022,6 +2022,24 @@ virStoragePRDefFormat(virBufferPtr buf, }
+bool +virStoragePRDefIsEqual(virStoragePRDefPtr a, + virStoragePRDefPtr b) +{ + if (!a && !b) + return true; + + if (!a || !b) + return false; + + if (a->enabled != b->enabled || + a->managed != b->managed || + STRNEQ_NULLABLE(a->path, b->path)) + return false; + + return true; +} +
two blank lines [...] Reviewed-by: John Ferlan <jferlan@redhat.com> John

The capability tracks if qemu has pr-manager-helper object. At this time don't actually detect if qemu has the capability. Not just yet. Only after the code is written the feature will be enabled. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 1 + src/qemu/qemu_capabilities.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 91b7aa31ec..efd468692a 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -468,6 +468,7 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, "virtio-tablet-ccw", "qcow2-luks", "pcie-pci-bridge", + "pr-manager-helper", ); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index bec28cae92..f0948e3016 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -452,6 +452,7 @@ typedef enum { QEMU_CAPS_DEVICE_VIRTIO_TABLET_CCW, /* -device virtio-tablet-ccw */ QEMU_CAPS_QCOW2_LUKS, /* qcow2 format support LUKS encryption */ QEMU_CAPS_DEVICE_PCIE_PCI_BRIDGE, /* -device pcie-pci-bridge */ + QEMU_CAPS_PR_MANAGER_HELPER, /* -object pr-manager-helper */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
The capability tracks if qemu has pr-manager-helper object. At this time don't actually detect if qemu has the capability. Not just yet. Only after the code is written the feature will be enabled.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 1 + src/qemu/qemu_capabilities.h | 1 + 2 files changed, 2 insertions(+)
Reviewed-by: John Ferlan <jferlan@redhat.com> John

While we're not generating the command line just yet (look for the next commits), we can generate the alias for pr-manager. A domain can have up to one managed pr-manager (in which case socket path is decided by libvirt and pr-helper is spawned by libvirt too), but up to ndisks of unmanaged ones (one per disk basically). In case of the former we can generate a simple alias and be sure it'll not conflict. But in case of the latter to avoid any conflicts let's generate alias that's based on something unique - like disk target. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt_private.syms | 2 ++ src/qemu/qemu_alias.c | 11 ++++++ src/qemu/qemu_alias.h | 2 ++ src/qemu/qemu_domain.c | 87 +++++++++++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_domain.h | 10 ++++++ src/util/virstoragefile.c | 15 ++++++++ src/util/virstoragefile.h | 2 ++ 7 files changed, 126 insertions(+), 3 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a376e3bb0d..5b7b5514a2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2799,7 +2799,9 @@ virStorageNetHostTransportTypeToString; virStorageNetProtocolTypeToString; virStoragePRDefFormat; virStoragePRDefFree; +virStoragePRDefIsEnabled; virStoragePRDefIsEqual; +virStoragePRDefIsManaged; virStoragePRDefParseXML; virStorageSourceBackingStoreClear; virStorageSourceClear; diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index 95d1e0370a..6db3da27a8 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -773,3 +773,14 @@ qemuAliasChardevFromDevAlias(const char *devAlias) return ret; } + + +char * +qemuDomainGetManagedPRAlias(void) +{ + char *alias; + + ignore_value(VIR_STRDUP(alias, "pr-helper0")); + + return alias; +} diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h index 8c744138ce..91e0a9c893 100644 --- a/src/qemu/qemu_alias.h +++ b/src/qemu/qemu_alias.h @@ -92,4 +92,6 @@ char *qemuAliasTLSObjFromSrcAlias(const char *srcAlias) char *qemuAliasChardevFromDevAlias(const char *devAlias) ATTRIBUTE_NONNULL(1); +char * qemuDomainGetManagedPRAlias(void); + #endif /* __QEMU_ALIAS_H__*/ diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 5a7b5f8417..361a80be84 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -982,6 +982,18 @@ qemuDomainSecretInfoFree(qemuDomainSecretInfoPtr *secinfo) } +static void +qemuDomainDiskPRDFree(qemuDomainDiskPRDPtr prd) +{ + if (!prd) + return; + + VIR_FREE(prd->alias); + VIR_FREE(prd->path); + VIR_FREE(prd); +} + + static virClassPtr qemuDomainDiskPrivateClass; static void qemuDomainDiskPrivateDispose(void *obj); @@ -1062,6 +1074,7 @@ qemuDomainStorageSourcePrivateDispose(void *obj) qemuDomainSecretInfoFree(&priv->secinfo); qemuDomainSecretInfoFree(&priv->encinfo); + qemuDomainDiskPRDFree(priv->prd); } @@ -1473,9 +1486,6 @@ qemuDomainSecretStorageSourcePrepare(qemuDomainObjPrivatePtr priv, if (!hasAuth && !hasEnc) return 0; - if (!(src->privateData = qemuDomainStorageSourcePrivateNew())) - return -1; - srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); if (hasAuth) { @@ -11925,11 +11935,79 @@ qemuDomainPrepareDiskCachemode(virDomainDiskDefPtr disk) } +static int +qemuDomainPrepareDiskPRD(qemuDomainObjPrivatePtr priv, + virDomainDiskDefPtr disk) +{ + qemuDomainStorageSourcePrivatePtr srcPriv; + virStoragePRDefPtr prd = disk->src->pr; + char *prAlias = NULL; + char *prPath = NULL; + int ret = -1; + + if (!virStoragePRDefIsEnabled(prd)) + return 0; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_PR_MANAGER_HELPER)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("reservations not supported with this QEMU binary")); + return ret; + } + + if (!virStorageSourceIsLocalStorage(disk->src)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("reservations supported only for local storage")); + return ret; + } + + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + + /* Managed PR means there's one pr-manager object per domain + * and the pr-helper process is spawned and managed by + * libvirt. + * If PR is unmanaged there's one pr-manager object per disk + * (in general each disk can have its own pr-manager) and + * it's user's responsibility to start the pr-helper process. + */ + if (virStoragePRDefIsManaged(prd)) { + /* Generate PR helper socket path & alias that are same + * for each disk in the domain. */ + + if (!(prAlias = qemuDomainGetManagedPRAlias())) + return ret; + + if (virAsprintf(&prPath, "%s/pr-helper0.sock", priv->libDir) < 0) + goto cleanup; + + } else { + if (virAsprintf(&prAlias, "pr-helper-%s", disk->info.alias) < 0) + return ret; + + if (VIR_STRDUP(prPath, prd->path) < 0) + goto cleanup; + } + + if (VIR_ALLOC(srcPriv->prd) < 0) + goto cleanup; + VIR_STEAL_PTR(srcPriv->prd->alias, prAlias); + VIR_STEAL_PTR(srcPriv->prd->path, prPath); + + ret = 0; + cleanup: + VIR_FREE(prPath); + VIR_FREE(prAlias); + return ret; +} + + int qemuDomainPrepareDiskSource(virDomainDiskDefPtr disk, qemuDomainObjPrivatePtr priv, virQEMUDriverConfigPtr cfg) { + if (!(disk->src->privateData = qemuDomainStorageSourcePrivateNew())) + return -1; + qemuDomainPrepareDiskCachemode(disk); if (qemuDomainPrepareDiskSourceTLS(disk->src, cfg) < 0) @@ -11938,6 +12016,9 @@ qemuDomainPrepareDiskSource(virDomainDiskDefPtr disk, if (qemuDomainSecretDiskPrepare(priv, disk) < 0) return -1; + if (qemuDomainPrepareDiskPRD(priv, disk) < 0) + return -1; + if (qemuDomainPrepareDiskSourceChain(disk, NULL, cfg, priv->qemuCaps) < 0) return -1; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 21e12f6594..d283f128ef 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -373,6 +373,13 @@ struct _qemuDomainDiskPrivate { bool removable; /* device media can be removed/changed */ }; +typedef struct _qemuDomainDiskPRD qemuDomainDiskPRD; +typedef qemuDomainDiskPRD *qemuDomainDiskPRDPtr; +struct _qemuDomainDiskPRD { + char *alias; + char *path; /* socket path. */ +}; + # define QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src) \ ((qemuDomainStorageSourcePrivatePtr) (src)->privateData) @@ -386,6 +393,9 @@ struct _qemuDomainStorageSourcePrivate { /* data required for decryption of encrypted storage source */ qemuDomainSecretInfoPtr encinfo; + + /* data required for persistent reservations */ + qemuDomainDiskPRDPtr prd; }; virObjectPtr qemuDomainStorageSourcePrivateNew(void); diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index b017024b2f..877d8ba8e4 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -2040,6 +2040,21 @@ virStoragePRDefIsEqual(virStoragePRDefPtr a, return true; } + +bool +virStoragePRDefIsEnabled(virStoragePRDefPtr prd) +{ + return prd && prd->enabled == VIR_TRISTATE_BOOL_YES; +} + + +bool +virStoragePRDefIsManaged(virStoragePRDefPtr prd) +{ + return prd && prd->managed == VIR_TRISTATE_BOOL_YES; +} + + virSecurityDeviceLabelDefPtr virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src, const char *model) diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 77853eefe8..2127509ea3 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -385,6 +385,8 @@ void virStoragePRDefFormat(virBufferPtr buf, virStoragePRDefPtr prd); bool virStoragePRDefIsEqual(virStoragePRDefPtr a, virStoragePRDefPtr b); +bool virStoragePRDefIsEnabled(virStoragePRDefPtr prd); +bool virStoragePRDefIsManaged(virStoragePRDefPtr prd); virSecurityDeviceLabelDefPtr virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src, -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
While we're not generating the command line just yet (look for the next commits), we can generate the alias for pr-manager. A domain can have up to one managed pr-manager (in which case socket path is decided by libvirt and pr-helper is spawned by libvirt too), but up to ndisks of unmanaged ones (one per disk basically). In case of the former we can generate a simple alias and be sure it'll not conflict. But in case of the latter to
Well if it's only one, then it had better not conflict (IOW: after the and is unnecessary because there is check).
avoid any conflicts let's generate alias that's based on something unique - like disk target.
Make sure this commit message follows whatever happens in this patch...
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt_private.syms | 2 ++ src/qemu/qemu_alias.c | 11 ++++++ src/qemu/qemu_alias.h | 2 ++ src/qemu/qemu_domain.c | 87 +++++++++++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_domain.h | 10 ++++++ src/util/virstoragefile.c | 15 ++++++++ src/util/virstoragefile.h | 2 ++ 7 files changed, 126 insertions(+), 3 deletions(-)
This patch does two things - one is just the pure alias/path for the pr-helper for the active domain. The second is copy that same information to the private storage source, which I'm not sure (yet) why it's necessary since both can be regenerated as needed based on fairly static data as opposed to secinfo and encinfo which get filled in with information only available during Prepare (the interaction with the secret driver and the need for the @conn pointer) that wasn't available during launch/command line building. Although some of that has changed with more recent changes to be able to get a secret conn "on the fly". Anyway, I digress. The point being - please note why you're also saving something in the private storage source area... The 'path' is already present in the domain XML (both active and inactive) and the 'alias' could be saved in the active output if you tweaked virStoragePRDefFormat to match what virDomainDeviceInfoFormat does.
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a376e3bb0d..5b7b5514a2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2799,7 +2799,9 @@ virStorageNetHostTransportTypeToString; virStorageNetProtocolTypeToString; virStoragePRDefFormat; virStoragePRDefFree; +virStoragePRDefIsEnabled; virStoragePRDefIsEqual; +virStoragePRDefIsManaged; virStoragePRDefParseXML; virStorageSourceBackingStoreClear; virStorageSourceClear; diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index 95d1e0370a..6db3da27a8 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -773,3 +773,14 @@ qemuAliasChardevFromDevAlias(const char *devAlias)
return ret; } + + +char * +qemuDomainGetManagedPRAlias(void) +{ + char *alias; + + ignore_value(VIR_STRDUP(alias, "pr-helper0")); + + return alias; +}
I don't really see a purpose for this function. If it were to survive, then it could take a parameter "const char *srcalias" and create/return the alias from that based on what's in qemuDomainPrepareDiskPRD. Eventually when the qemuProcessKillPRDaemon is created, it doesn't necessarily distinguish between managed and unmanaged... Still having it fail because it couldn't strdup what amounts to be a static string doesn't make much sense. diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h
index 8c744138ce..91e0a9c893 100644 --- a/src/qemu/qemu_alias.h +++ b/src/qemu/qemu_alias.h @@ -92,4 +92,6 @@ char *qemuAliasTLSObjFromSrcAlias(const char *srcAlias) char *qemuAliasChardevFromDevAlias(const char *devAlias) ATTRIBUTE_NONNULL(1);
+char * qemuDomainGetManagedPRAlias(void); + #endif /* __QEMU_ALIAS_H__*/ diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 5a7b5f8417..361a80be84 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -982,6 +982,18 @@ qemuDomainSecretInfoFree(qemuDomainSecretInfoPtr *secinfo) }
+static void +qemuDomainDiskPRDFree(qemuDomainDiskPRDPtr prd) +{ + if (!prd) + return; + + VIR_FREE(prd->alias); + VIR_FREE(prd->path); + VIR_FREE(prd); +} + + static virClassPtr qemuDomainDiskPrivateClass; static void qemuDomainDiskPrivateDispose(void *obj);
@@ -1062,6 +1074,7 @@ qemuDomainStorageSourcePrivateDispose(void *obj)
qemuDomainSecretInfoFree(&priv->secinfo); qemuDomainSecretInfoFree(&priv->encinfo); + qemuDomainDiskPRDFree(priv->prd); }
@@ -1473,9 +1486,6 @@ qemuDomainSecretStorageSourcePrepare(qemuDomainObjPrivatePtr priv, if (!hasAuth && !hasEnc) return 0;
- if (!(src->privateData = qemuDomainStorageSourcePrivateNew())) - return -1; - srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
if (hasAuth) { @@ -11925,11 +11935,79 @@ qemuDomainPrepareDiskCachemode(virDomainDiskDefPtr disk) }
+static int +qemuDomainPrepareDiskPRD(qemuDomainObjPrivatePtr priv, + virDomainDiskDefPtr disk) +{ + qemuDomainStorageSourcePrivatePtr srcPriv; + virStoragePRDefPtr prd = disk->src->pr; + char *prAlias = NULL; + char *prPath = NULL; + int ret = -1; + + if (!virStoragePRDefIsEnabled(prd)) + return 0; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_PR_MANAGER_HELPER)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("reservations not supported with this QEMU binary")); + return ret; + } + + if (!virStorageSourceIsLocalStorage(disk->src)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("reservations supported only for local storage")); + return ret; + }
Not only local, but local and LUN ... Still this check probably should have gone into the Validate code if anything.
+ + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + + /* Managed PR means there's one pr-manager object per domain + * and the pr-helper process is spawned and managed by + * libvirt. + * If PR is unmanaged there's one pr-manager object per disk + * (in general each disk can have its own pr-manager) and + * it's user's responsibility to start the pr-helper process. + */ + if (virStoragePRDefIsManaged(prd)) { + /* Generate PR helper socket path & alias that are same + * for each disk in the domain. */ + + if (!(prAlias = qemuDomainGetManagedPRAlias()))
just make it a straight VIR_STRDUP("pr-helper0") or some sort of #define for VIRT_PR_MANAGED_ALIAS that's usable by qemuProcessKillPRDaemon... You could go really obscure by using "pr-helper" as the defined value and then using that in all your various printf's. John
+ return ret; + + if (virAsprintf(&prPath, "%s/pr-helper0.sock", priv->libDir) < 0) + goto cleanup; + + } else { + if (virAsprintf(&prAlias, "pr-helper-%s", disk->info.alias) < 0) + return ret; + + if (VIR_STRDUP(prPath, prd->path) < 0) + goto cleanup; + } + + if (VIR_ALLOC(srcPriv->prd) < 0) + goto cleanup; + VIR_STEAL_PTR(srcPriv->prd->alias, prAlias); + VIR_STEAL_PTR(srcPriv->prd->path, prPath); + + ret = 0; + cleanup: + VIR_FREE(prPath); + VIR_FREE(prAlias); + return ret; +} + + int qemuDomainPrepareDiskSource(virDomainDiskDefPtr disk, qemuDomainObjPrivatePtr priv, virQEMUDriverConfigPtr cfg) { + if (!(disk->src->privateData = qemuDomainStorageSourcePrivateNew())) + return -1; + qemuDomainPrepareDiskCachemode(disk);
if (qemuDomainPrepareDiskSourceTLS(disk->src, cfg) < 0) @@ -11938,6 +12016,9 @@ qemuDomainPrepareDiskSource(virDomainDiskDefPtr disk, if (qemuDomainSecretDiskPrepare(priv, disk) < 0) return -1;
+ if (qemuDomainPrepareDiskPRD(priv, disk) < 0) + return -1; + if (qemuDomainPrepareDiskSourceChain(disk, NULL, cfg, priv->qemuCaps) < 0) return -1;
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 21e12f6594..d283f128ef 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -373,6 +373,13 @@ struct _qemuDomainDiskPrivate { bool removable; /* device media can be removed/changed */ };
+typedef struct _qemuDomainDiskPRD qemuDomainDiskPRD; +typedef qemuDomainDiskPRD *qemuDomainDiskPRDPtr; +struct _qemuDomainDiskPRD { + char *alias; + char *path; /* socket path. */ +}; + # define QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src) \ ((qemuDomainStorageSourcePrivatePtr) (src)->privateData)
@@ -386,6 +393,9 @@ struct _qemuDomainStorageSourcePrivate {
/* data required for decryption of encrypted storage source */ qemuDomainSecretInfoPtr encinfo; + + /* data required for persistent reservations */ + qemuDomainDiskPRDPtr prd; };
virObjectPtr qemuDomainStorageSourcePrivateNew(void); diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index b017024b2f..877d8ba8e4 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -2040,6 +2040,21 @@ virStoragePRDefIsEqual(virStoragePRDefPtr a, return true; }
+ +bool +virStoragePRDefIsEnabled(virStoragePRDefPtr prd) +{ + return prd && prd->enabled == VIR_TRISTATE_BOOL_YES; +} + + +bool +virStoragePRDefIsManaged(virStoragePRDefPtr prd) +{ + return prd && prd->managed == VIR_TRISTATE_BOOL_YES; +} + + virSecurityDeviceLabelDefPtr virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src, const char *model) diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 77853eefe8..2127509ea3 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -385,6 +385,8 @@ void virStoragePRDefFormat(virBufferPtr buf, virStoragePRDefPtr prd); bool virStoragePRDefIsEqual(virStoragePRDefPtr a, virStoragePRDefPtr b); +bool virStoragePRDefIsEnabled(virStoragePRDefPtr prd); +bool virStoragePRDefIsManaged(virStoragePRDefPtr prd);
virSecurityDeviceLabelDefPtr virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src,

On 04/13/2018 10:51 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
While we're not generating the command line just yet (look for the next commits), we can generate the alias for pr-manager. A domain can have up to one managed pr-manager (in which case socket path is decided by libvirt and pr-helper is spawned by libvirt too), but up to ndisks of unmanaged ones (one per disk basically). In case of the former we can generate a simple alias and be sure it'll not conflict. But in case of the latter to
Well if it's only one, then it had better not conflict (IOW: after the and is unnecessary because there is check).
avoid any conflicts let's generate alias that's based on something unique - like disk target.
Make sure this commit message follows whatever happens in this patch...
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt_private.syms | 2 ++ src/qemu/qemu_alias.c | 11 ++++++ src/qemu/qemu_alias.h | 2 ++ src/qemu/qemu_domain.c | 87 +++++++++++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_domain.h | 10 ++++++ src/util/virstoragefile.c | 15 ++++++++ src/util/virstoragefile.h | 2 ++ 7 files changed, 126 insertions(+), 3 deletions(-)
This patch does two things - one is just the pure alias/path for the pr-helper for the active domain. The second is copy that same information to the private storage source, which I'm not sure (yet) why it's necessary since both can be regenerated as needed based on fairly static data as opposed to secinfo and encinfo which get filled in with information only available during Prepare (the interaction with the secret driver and the need for the @conn pointer) that wasn't available during launch/command line building. Although some of that has changed with more recent changes to be able to get a secret conn "on the fly". Anyway, I digress.
The point being - please note why you're also saving something in the private storage source area... The 'path' is already present in the domain XML (both active and inactive) and the 'alias' could be saved in the active output if you tweaked virStoragePRDefFormat to match what virDomainDeviceInfoFormat does.
Okay. I will put it in the commit message. I thought we've discussed this earlier. I rather put and info into status XML even though it might look useless right now than having to write some crazy backcompat code later.
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a376e3bb0d..5b7b5514a2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2799,7 +2799,9 @@ virStorageNetHostTransportTypeToString; virStorageNetProtocolTypeToString; virStoragePRDefFormat; virStoragePRDefFree; +virStoragePRDefIsEnabled; virStoragePRDefIsEqual; +virStoragePRDefIsManaged; virStoragePRDefParseXML; virStorageSourceBackingStoreClear; virStorageSourceClear; diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index 95d1e0370a..6db3da27a8 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -773,3 +773,14 @@ qemuAliasChardevFromDevAlias(const char *devAlias)
return ret; } + + +char * +qemuDomainGetManagedPRAlias(void) +{ + char *alias; + + ignore_value(VIR_STRDUP(alias, "pr-helper0")); + + return alias; +}
I don't really see a purpose for this function. If it were to survive, then it could take a parameter "const char *srcalias" and create/return the alias from that based on what's in qemuDomainPrepareDiskPRD.
The purpose is to generate alias at only one place instead of several virAsprintf-s scattered all around the code.
Eventually when the qemuProcessKillPRDaemon is created, it doesn't necessarily distinguish between managed and unmanaged...
Well, unmanaged helper should not have its socket in libvirt private path. Under any circumstances. And what qemuProcessKillPRDaemon does is: 1) it constructs PID file path 2) and kills any pr-helper that might had been left behind. I find this approach more robust than plain "check if there is a disk with pr-helper and if so kill it" because if libvirt is ever split brained
Still having it fail because it couldn't strdup what amounts to be a static string doesn't make much sense.
Well, there are couple of reasons for stdup()-ing a static string: a) it simplifies the code, e.g. qemuDomainDiskPRDFree() doesn't have to care if alias is a static string or dynamically allocated one, b) higher memory consumption (by 11 bytes btw) is only for a short period of time (when constructing PID file path). Although, the alias is kept around for entire time domain is running. And if libvirt is unable to allocate 11 bytes then you're in much bigger trouble anyways.
diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h
index 8c744138ce..91e0a9c893 100644 --- a/src/qemu/qemu_alias.h +++ b/src/qemu/qemu_alias.h @@ -92,4 +92,6 @@ char *qemuAliasTLSObjFromSrcAlias(const char *srcAlias) char *qemuAliasChardevFromDevAlias(const char *devAlias) ATTRIBUTE_NONNULL(1);
+char * qemuDomainGetManagedPRAlias(void); + #endif /* __QEMU_ALIAS_H__*/ diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 5a7b5f8417..361a80be84 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -982,6 +982,18 @@ qemuDomainSecretInfoFree(qemuDomainSecretInfoPtr *secinfo) }
+static void +qemuDomainDiskPRDFree(qemuDomainDiskPRDPtr prd) +{ + if (!prd) + return; + + VIR_FREE(prd->alias); + VIR_FREE(prd->path); + VIR_FREE(prd); +} + + static virClassPtr qemuDomainDiskPrivateClass; static void qemuDomainDiskPrivateDispose(void *obj);
@@ -1062,6 +1074,7 @@ qemuDomainStorageSourcePrivateDispose(void *obj)
qemuDomainSecretInfoFree(&priv->secinfo); qemuDomainSecretInfoFree(&priv->encinfo); + qemuDomainDiskPRDFree(priv->prd); }
@@ -1473,9 +1486,6 @@ qemuDomainSecretStorageSourcePrepare(qemuDomainObjPrivatePtr priv, if (!hasAuth && !hasEnc) return 0;
- if (!(src->privateData = qemuDomainStorageSourcePrivateNew())) - return -1; - srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
if (hasAuth) { @@ -11925,11 +11935,79 @@ qemuDomainPrepareDiskCachemode(virDomainDiskDefPtr disk) }
+static int +qemuDomainPrepareDiskPRD(qemuDomainObjPrivatePtr priv, + virDomainDiskDefPtr disk) +{ + qemuDomainStorageSourcePrivatePtr srcPriv; + virStoragePRDefPtr prd = disk->src->pr; + char *prAlias = NULL; + char *prPath = NULL; + int ret = -1; + + if (!virStoragePRDefIsEnabled(prd)) + return 0; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_PR_MANAGER_HELPER)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("reservations not supported with this QEMU binary")); + return ret; + } + + if (!virStorageSourceIsLocalStorage(disk->src)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("reservations supported only for local storage")); + return ret; + }
Not only local, but local and LUN ... Still this check probably should have gone into the Validate code if anything.
I don't think it can go there. For IsLocalStorage() to return valid results virDomainDiskTranslateSourcePool() needs to be called first. And it is done so only when starting domain. Not for Validate() callback. And technically, at this point whether disk is LUN or not is checked in virDomainDiskDefValidate(). So the control would not even get here. But okay, I will change the error message.
+ + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + + /* Managed PR means there's one pr-manager object per domain + * and the pr-helper process is spawned and managed by + * libvirt. + * If PR is unmanaged there's one pr-manager object per disk + * (in general each disk can have its own pr-manager) and + * it's user's responsibility to start the pr-helper process. + */ + if (virStoragePRDefIsManaged(prd)) { + /* Generate PR helper socket path & alias that are same + * for each disk in the domain. */ + + if (!(prAlias = qemuDomainGetManagedPRAlias()))
just make it a straight VIR_STRDUP("pr-helper0")
No. Having alias generation at single location is very advantageous IMO. Libvirt's full of small wrappers over functions. And it this case it even makes sense.
or some sort of #define for VIRT_PR_MANAGED_ALIAS that's usable by qemuProcessKillPRDaemon... You could go really obscure by using "pr-helper" as the defined value and then using that in all your various printf's.
Then I'd have to modify qemuDomainDiskPRDFree() so that for instance it does: if (STRNEQ(prd->alias, QEMU_PR_MANAGER_ALIAS)) VIR_FREE(prd->alias); VIR_FREE(prd->path); ... which I don't quite like (to say the least). Michal

On 04/16/2018 10:56 AM, Michal Privoznik wrote:
On 04/13/2018 10:51 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
While we're not generating the command line just yet (look for the next commits), we can generate the alias for pr-manager. A domain can have up to one managed pr-manager (in which case socket path is decided by libvirt and pr-helper is spawned by libvirt too), but up to ndisks of unmanaged ones (one per disk basically). In case of the former we can generate a simple alias and be sure it'll not conflict. But in case of the latter to
Well if it's only one, then it had better not conflict (IOW: after the and is unnecessary because there is check).
avoid any conflicts let's generate alias that's based on something unique - like disk target.
Make sure this commit message follows whatever happens in this patch...
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt_private.syms | 2 ++ src/qemu/qemu_alias.c | 11 ++++++ src/qemu/qemu_alias.h | 2 ++ src/qemu/qemu_domain.c | 87 +++++++++++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_domain.h | 10 ++++++ src/util/virstoragefile.c | 15 ++++++++ src/util/virstoragefile.h | 2 ++ 7 files changed, 126 insertions(+), 3 deletions(-)
This patch does two things - one is just the pure alias/path for the pr-helper for the active domain. The second is copy that same information to the private storage source, which I'm not sure (yet) why it's necessary since both can be regenerated as needed based on fairly static data as opposed to secinfo and encinfo which get filled in with information only available during Prepare (the interaction with the secret driver and the need for the @conn pointer) that wasn't available during launch/command line building. Although some of that has changed with more recent changes to be able to get a secret conn "on the fly". Anyway, I digress.
The point being - please note why you're also saving something in the private storage source area... The 'path' is already present in the domain XML (both active and inactive) and the 'alias' could be saved in the active output if you tweaked virStoragePRDefFormat to match what virDomainDeviceInfoFormat does.
Okay. I will put it in the commit message. I thought we've discussed this earlier. I rather put and info into status XML even though it might look useless right now than having to write some crazy backcompat code later.
I don't think this is the same as @channelTargetDir (as you note in the next response). The problem there was a reliance on a name that ended up being too long and yes, it was a mess. I dunno - I think I've tried the future proofing things too and was told it was unnecessary. Still in order to "provide insight" into my why... Here you have a @path that's essentially static outside of the commonly used priv->libDir and an @alias that is static when managed. When unmanaged, the @path is provided by a consumer and the @alias is essentially a static prefix followed by a commonly used source destination alias. So nothing so far that would need to be saved in two places. Since @alias is an "internal" libvirt->qemu construct we wouldn't have the same problem as some "external" construct changing our definition. Unlike perhaps _qemuDomainVcpuPrivate where the alias perhaps changes based on ordering, insert, remove, etc. (I didn't look, but made the assumption). For PR there is one and it's not going to change. Even if it did, we would always know the former format to search on before using a newer format. In the long run, since there would be only one the PR alias belongs in the _qemuDomainObjPrivate further removing the need to have anything private. As noted somewhere during my review the existing need for a private structure for secrets could now be reworked/removed because the code now can generate the @conn on the fly rather than needing the @conn from the original connection in order to lookup the secret. Depends on if we want an error during command line generation or during the prepare phase.
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a376e3bb0d..5b7b5514a2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2799,7 +2799,9 @@ virStorageNetHostTransportTypeToString; virStorageNetProtocolTypeToString; virStoragePRDefFormat; virStoragePRDefFree; +virStoragePRDefIsEnabled; virStoragePRDefIsEqual; +virStoragePRDefIsManaged; virStoragePRDefParseXML; virStorageSourceBackingStoreClear; virStorageSourceClear; diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index 95d1e0370a..6db3da27a8 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -773,3 +773,14 @@ qemuAliasChardevFromDevAlias(const char *devAlias)
return ret; } + + +char * +qemuDomainGetManagedPRAlias(void) +{ + char *alias; + + ignore_value(VIR_STRDUP(alias, "pr-helper0")); + + return alias; +}
I don't really see a purpose for this function. If it were to survive, then it could take a parameter "const char *srcalias" and create/return the alias from that based on what's in qemuDomainPrepareDiskPRD.
The purpose is to generate alias at only one place instead of several virAsprintf-s scattered all around the code.
ug, sure this is similar to qemuDomainGetMasterKeyAlias or qemuAssignDeviceWatchdogAlias. Since there can only ever be one managed PR per domain, then scattered is I think limited to two places - start and stop.
Eventually when the qemuProcessKillPRDaemon is created, it doesn't necessarily distinguish between managed and unmanaged...
Well, unmanaged helper should not have its socket in libvirt private path. Under any circumstances.
The KillPRDaemon doesn't distinguish between managed/unmanaged, but callers do - perhaps wasn't as clear yet when I noted that...
And what qemuProcessKillPRDaemon does is: 1) it constructs PID file path 2) and kills any pr-helper that might had been left behind.
I find this approach more robust than plain "check if there is a disk with pr-helper and if so kill it" because if libvirt is ever split brained
Jumping forward 6 patches. Maybe it would have helped to have all the logic that would create a PR process "in place". Then add the disk logic. The two concepts are intertwined and related, but it is possible to separate too.
Still having it fail because it couldn't strdup what amounts to be a static string doesn't make much sense.
Well, there are couple of reasons for stdup()-ing a static string: a) it simplifies the code, e.g. qemuDomainDiskPRDFree() doesn't have to care if alias is a static string or dynamically allocated one,
I don't think I was advocating usage of a static string to be saved in the ->alias field. I was merely commenting on the need to have a helper to perform the strdup of a static string that could be some #define somewhere.... IOW, later on it's: if (VIR_STRDUP(prAlias, VIRT_PR_MANAGED_ALIAS) < 0) instead of if (!(prAlias = qemuDomainGetManagedPRAlias()))
b) higher memory consumption (by 11 bytes btw) is only for a short period of time (when constructing PID file path). Although, the alias is kept around for entire time domain is running.
And if libvirt is unable to allocate 11 bytes then you're in much bigger trouble anyways.
11 here, 11 there, it all adds up. But no, it's not the 11 or probably more like 16, 32, 64, etc. whatever the underlying code would consider it's smallest hunk. Still if you had 10 managed LUNs, that's a lot of replicated data.
diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h
index 8c744138ce..91e0a9c893 100644 --- a/src/qemu/qemu_alias.h +++ b/src/qemu/qemu_alias.h @@ -92,4 +92,6 @@ char *qemuAliasTLSObjFromSrcAlias(const char *srcAlias) char *qemuAliasChardevFromDevAlias(const char *devAlias) ATTRIBUTE_NONNULL(1);
+char * qemuDomainGetManagedPRAlias(void); + #endif /* __QEMU_ALIAS_H__*/ diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 5a7b5f8417..361a80be84 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -982,6 +982,18 @@ qemuDomainSecretInfoFree(qemuDomainSecretInfoPtr *secinfo) }
+static void +qemuDomainDiskPRDFree(qemuDomainDiskPRDPtr prd) +{ + if (!prd) + return; + + VIR_FREE(prd->alias); + VIR_FREE(prd->path); + VIR_FREE(prd); +} + + static virClassPtr qemuDomainDiskPrivateClass; static void qemuDomainDiskPrivateDispose(void *obj);
@@ -1062,6 +1074,7 @@ qemuDomainStorageSourcePrivateDispose(void *obj)
qemuDomainSecretInfoFree(&priv->secinfo); qemuDomainSecretInfoFree(&priv->encinfo); + qemuDomainDiskPRDFree(priv->prd); }
@@ -1473,9 +1486,6 @@ qemuDomainSecretStorageSourcePrepare(qemuDomainObjPrivatePtr priv, if (!hasAuth && !hasEnc) return 0;
- if (!(src->privateData = qemuDomainStorageSourcePrivateNew())) - return -1; - srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
if (hasAuth) { @@ -11925,11 +11935,79 @@ qemuDomainPrepareDiskCachemode(virDomainDiskDefPtr disk) }
+static int +qemuDomainPrepareDiskPRD(qemuDomainObjPrivatePtr priv, + virDomainDiskDefPtr disk) +{ + qemuDomainStorageSourcePrivatePtr srcPriv; + virStoragePRDefPtr prd = disk->src->pr; + char *prAlias = NULL; + char *prPath = NULL; + int ret = -1; + + if (!virStoragePRDefIsEnabled(prd)) + return 0; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_PR_MANAGER_HELPER)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("reservations not supported with this QEMU binary")); + return ret; + } + + if (!virStorageSourceIsLocalStorage(disk->src)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("reservations supported only for local storage")); + return ret; + }
Not only local, but local and LUN ... Still this check probably should have gone into the Validate code if anything.
I don't think it can go there. For IsLocalStorage() to return valid results virDomainDiskTranslateSourcePool() needs to be called first. And it is done so only when starting domain. Not for Validate() callback.
ah yes, the volume lun conundrum, sigh. John
And technically, at this point whether disk is LUN or not is checked in virDomainDiskDefValidate(). So the control would not even get here. But okay, I will change the error message.
+ + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + + /* Managed PR means there's one pr-manager object per domain + * and the pr-helper process is spawned and managed by + * libvirt. + * If PR is unmanaged there's one pr-manager object per disk + * (in general each disk can have its own pr-manager) and + * it's user's responsibility to start the pr-helper process. + */ + if (virStoragePRDefIsManaged(prd)) { + /* Generate PR helper socket path & alias that are same + * for each disk in the domain. */ + + if (!(prAlias = qemuDomainGetManagedPRAlias()))
just make it a straight VIR_STRDUP("pr-helper0")
No. Having alias generation at single location is very advantageous IMO. Libvirt's full of small wrappers over functions. And it this case it even makes sense.
or some sort of #define for VIRT_PR_MANAGED_ALIAS that's usable by qemuProcessKillPRDaemon... You could go really obscure by using "pr-helper" as the defined value and then using that in all your various printf's.
Then I'd have to modify qemuDomainDiskPRDFree() so that for instance it does:
if (STRNEQ(prd->alias, QEMU_PR_MANAGER_ALIAS)) VIR_FREE(prd->alias); VIR_FREE(prd->path); ...
which I don't quite like (to say the least).
Michal

On 04/17/2018 01:49 PM, John Ferlan wrote:
On 04/16/2018 10:56 AM, Michal Privoznik wrote:
On 04/13/2018 10:51 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
While we're not generating the command line just yet (look for the next commits), we can generate the alias for pr-manager. A domain can have up to one managed pr-manager (in which case socket path is decided by libvirt and pr-helper is spawned by libvirt too), but up to ndisks of unmanaged ones (one per disk basically). In case of the former we can generate a simple alias and be sure it'll not conflict. But in case of the latter to
Well if it's only one, then it had better not conflict (IOW: after the and is unnecessary because there is check).
avoid any conflicts let's generate alias that's based on something unique - like disk target.
Make sure this commit message follows whatever happens in this patch...
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt_private.syms | 2 ++ src/qemu/qemu_alias.c | 11 ++++++ src/qemu/qemu_alias.h | 2 ++ src/qemu/qemu_domain.c | 87 +++++++++++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_domain.h | 10 ++++++ src/util/virstoragefile.c | 15 ++++++++ src/util/virstoragefile.h | 2 ++ 7 files changed, 126 insertions(+), 3 deletions(-)
This patch does two things - one is just the pure alias/path for the pr-helper for the active domain. The second is copy that same information to the private storage source, which I'm not sure (yet) why it's necessary since both can be regenerated as needed based on fairly static data as opposed to secinfo and encinfo which get filled in with information only available during Prepare (the interaction with the secret driver and the need for the @conn pointer) that wasn't available during launch/command line building. Although some of that has changed with more recent changes to be able to get a secret conn "on the fly". Anyway, I digress.
The point being - please note why you're also saving something in the private storage source area... The 'path' is already present in the domain XML (both active and inactive) and the 'alias' could be saved in the active output if you tweaked virStoragePRDefFormat to match what virDomainDeviceInfoFormat does.
Okay. I will put it in the commit message. I thought we've discussed this earlier. I rather put and info into status XML even though it might look useless right now than having to write some crazy backcompat code later.
I don't think this is the same as @channelTargetDir (as you note in the next response). The problem there was a reliance on a name that ended up being too long and yes, it was a mess. I dunno - I think I've tried the future proofing things too and was told it was unnecessary.
Depends on problem you were trying to future proof from.
Still in order to "provide insight" into my why... Here you have a @path that's essentially static outside of the commonly used priv->libDir and an @alias that is static when managed. When unmanaged, the @path is provided by a consumer and the @alias is essentially a static prefix followed by a commonly used source destination alias. So nothing so far that would need to be saved in two places.
Not true. If alias were a static string, then _qemuDomainDiskPRD would need to reflect that. That means, alias must be 'const char *' because storing a static string into 'char *' is a big NO NO. But at the same time, alias is dynamically generated (for unmanaged disks), so it has to be 'char *'. Sure, we can obfuscate it by inventing new struct member, but just look at how ugly it looks: struct qemuDomainDiskPRD { char *alias; const char *constAlias; char *path; }; Also, think of all those special casing in the code that uses the struct. This approach that I implemented here is used widely across our code base: qemuAssignDeviceTPMAlias(), qemuAssignDeviceWatchdogAlias() to name few. Why we even need this struct you ask? Couple of reasons: a) this approach implemented here is specific to QEMU and QEMU only. It isn't hypervisor agnostic, thus it doesn't belong to _virStoragePRDef. b) we should separate runtime and config data. That's why virStorage*() functions live under src/util/ as they work with config and not runtime (as in hypervisor specific). This merely mimics vm->privateData abstraction.
Since @alias is an "internal" libvirt->qemu construct we wouldn't have the same problem as some "external" construct changing our definition. Unlike perhaps _qemuDomainVcpuPrivate where the alias perhaps changes based on ordering, insert, remove, etc. (I didn't look, but made the assumption). For PR there is one and it's not going to change. Even if it did, we would always know the former format to search on before using a newer format.
Why? We have an option to record alias used so that when libvirtd restarts we can read it back. On the other hand what you are proposing is to try current version, if that fails try all the previous. What for? to save couple of bytes of memory/disk space? I don't see added value there, sorry. Also, the whole point of status XML is so that we don't have to use try-error approach. Look at it from different angle: all devices have their alias generated from info contained in config XML (either based on their position like deviceN for N-th device, or based on their address like scsi-0-0-0-1 for corresponding SCSI address). We can regenerate them when reconnecting to qemu. Even better - we can ask on qemu monitor. So why not do that and waste memory/disk space by storing them in status XML?
In the long run, since there would be only one the PR alias belongs in the _qemuDomainObjPrivate further removing the need to have anything private.
As noted somewhere during my review the existing need for a private structure for secrets could now be reworked/removed because the code now can generate the @conn on the fly rather than needing the @conn from the original connection in order to lookup the secret. Depends on if we want an error during command line generation or during the prepare phase.
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a376e3bb0d..5b7b5514a2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2799,7 +2799,9 @@ virStorageNetHostTransportTypeToString; virStorageNetProtocolTypeToString; virStoragePRDefFormat; virStoragePRDefFree; +virStoragePRDefIsEnabled; virStoragePRDefIsEqual; +virStoragePRDefIsManaged; virStoragePRDefParseXML; virStorageSourceBackingStoreClear; virStorageSourceClear; diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index 95d1e0370a..6db3da27a8 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -773,3 +773,14 @@ qemuAliasChardevFromDevAlias(const char *devAlias)
return ret; } + + +char * +qemuDomainGetManagedPRAlias(void) +{ + char *alias; + + ignore_value(VIR_STRDUP(alias, "pr-helper0")); + + return alias; +}
I don't really see a purpose for this function. If it were to survive, then it could take a parameter "const char *srcalias" and create/return the alias from that based on what's in qemuDomainPrepareDiskPRD.
The purpose is to generate alias at only one place instead of several virAsprintf-s scattered all around the code.
ug, sure this is similar to qemuDomainGetMasterKeyAlias or qemuAssignDeviceWatchdogAlias. Since there can only ever be one managed PR per domain, then scattered is I think limited to two places - start and stop.
So? It is still better to have it at one place and called from multiple.
Eventually when the qemuProcessKillPRDaemon is created, it doesn't necessarily distinguish between managed and unmanaged...
Well, unmanaged helper should not have its socket in libvirt private path. Under any circumstances.
The KillPRDaemon doesn't distinguish between managed/unmanaged, but callers do - perhaps wasn't as clear yet when I noted that...
And what qemuProcessKillPRDaemon does is: 1) it constructs PID file path 2) and kills any pr-helper that might had been left behind.
I find this approach more robust than plain "check if there is a disk with pr-helper and if so kill it" because if libvirt is ever split brained
Jumping forward 6 patches. Maybe it would have helped to have all the logic that would create a PR process "in place". Then add the disk logic. The two concepts are intertwined and related, but it is possible to separate too.
I guess it makes perfect sense as it is now.
Still having it fail because it couldn't strdup what amounts to be a static string doesn't make much sense.
Well, there are couple of reasons for stdup()-ing a static string: a) it simplifies the code, e.g. qemuDomainDiskPRDFree() doesn't have to care if alias is a static string or dynamically allocated one,
I don't think I was advocating usage of a static string to be saved in the ->alias field. I was merely commenting on the need to have a helper to perform the strdup of a static string that could be some #define somewhere.... IOW, later on it's:
if (VIR_STRDUP(prAlias, VIRT_PR_MANAGED_ALIAS) < 0)
instead of
if (!(prAlias = qemuDomainGetManagedPRAlias()))
b) higher memory consumption (by 11 bytes btw) is only for a short period of time (when constructing PID file path). Although, the alias is kept around for entire time domain is running.
And if libvirt is unable to allocate 11 bytes then you're in much bigger trouble anyways.
11 here, 11 there, it all adds up. But no, it's not the 11 or probably more like 16, 32, 64, etc. whatever the underlying code would consider it's smallest hunk.
Still if you had 10 managed LUNs, that's a lot of replicated data.
Read my reply above. Obfuscating code just to save couple of bytes of memory is not a good idea in my book. Michal

Now that we generate pr-manager alias and socket path store them in status XML so that they are preserved across daemon restarts. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_domain.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 361a80be84..0856f04406 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1961,13 +1961,74 @@ qemuDomainObjPrivateFree(void *data) } +static int +qemuStorageSourcePrivateDataParsePR(xmlXPathContextPtr ctxt, + virStorageSourcePtr src) +{ + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); + qemuDomainDiskPRDPtr prd = NULL; + int rc; + int ret = -1; + + if ((rc = virXPathBoolean("boolean(./prd)", ctxt)) == 0) { + return 0; + } else if (rc < 0) { + return ret; + } + + if (VIR_ALLOC(prd) < 0) + return ret; + + if (!(prd->alias = virXPathString("string(./prd/alias)", ctxt)) || + !(prd->path = virXPathString("string(./prd/path)", ctxt))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed <prd/>")); + goto cleanup; + } + + VIR_STEAL_PTR(srcPriv->prd, prd); + ret = 0; + cleanup: + qemuDomainDiskPRDFree(prd); + return ret; +} + + +static int +qemuStorageSourcePrivateDataFormatPR(virStorageSourcePtr src, + virBufferPtr buf) +{ + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); + qemuDomainDiskPRDPtr prd; + + if (!srcPriv || !srcPriv->prd) + return 0; + + prd = srcPriv->prd; + + virBufferAddLit(buf, "<prd>\n"); + virBufferAdjustIndent(buf, 2); + virBufferAsprintf(buf, "<alias>%s</alias>\n", prd->alias); + virBufferEscapeString(buf, "<path>%s</path>\n", prd->path); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</prd>\n"); + return 0; +} + + static int qemuStorageSourcePrivateDataParse(xmlXPathContextPtr ctxt, virStorageSourcePtr src) { + if (!(src->privateData = qemuDomainStorageSourcePrivateNew())) + return -1; + if (virStorageSourcePrivateDataParseRelPath(ctxt, src) < 0) return -1; + if (qemuStorageSourcePrivateDataParsePR(ctxt, src) < 0) + return -1; + return 0; } @@ -1979,6 +2040,9 @@ qemuStorageSourcePrivateDataFormat(virStorageSourcePtr src, if (virStorageSourcePrivateDataFormatRelPath(src, buf) < 0) return -1; + if (qemuStorageSourcePrivateDataFormatPR(src, buf) < 0) + return -1; + return 0; } -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Now that we generate pr-manager alias and socket path store them in status XML so that they are preserved across daemon restarts.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_domain.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)
So if we were to save this information (and at this point I don't think we need to), then this is where we should be allocating and filling in the private data (e.g. not in the previous patch). Again as I noted previously, save the alias when printing the domain active information and I think you're good. AFAICT the only thing printed now (@relPath) is something generated via qemu_driver calls (I didn't dig deep); whereas, this data is easily regeneratable from existing domain definition data. John
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 361a80be84..0856f04406 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1961,13 +1961,74 @@ qemuDomainObjPrivateFree(void *data) }
+static int +qemuStorageSourcePrivateDataParsePR(xmlXPathContextPtr ctxt, + virStorageSourcePtr src) +{ + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); + qemuDomainDiskPRDPtr prd = NULL; + int rc; + int ret = -1; + + if ((rc = virXPathBoolean("boolean(./prd)", ctxt)) == 0) { + return 0; + } else if (rc < 0) { + return ret; + } + + if (VIR_ALLOC(prd) < 0) + return ret; + + if (!(prd->alias = virXPathString("string(./prd/alias)", ctxt)) || + !(prd->path = virXPathString("string(./prd/path)", ctxt))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed <prd/>")); + goto cleanup; + } + + VIR_STEAL_PTR(srcPriv->prd, prd); + ret = 0; + cleanup: + qemuDomainDiskPRDFree(prd); + return ret; +} + + +static int +qemuStorageSourcePrivateDataFormatPR(virStorageSourcePtr src, + virBufferPtr buf) +{ + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); + qemuDomainDiskPRDPtr prd; + + if (!srcPriv || !srcPriv->prd) + return 0; + + prd = srcPriv->prd; + + virBufferAddLit(buf, "<prd>\n"); + virBufferAdjustIndent(buf, 2); + virBufferAsprintf(buf, "<alias>%s</alias>\n", prd->alias); + virBufferEscapeString(buf, "<path>%s</path>\n", prd->path); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</prd>\n"); + return 0; +} + + static int qemuStorageSourcePrivateDataParse(xmlXPathContextPtr ctxt, virStorageSourcePtr src) { + if (!(src->privateData = qemuDomainStorageSourcePrivateNew())) + return -1; + if (virStorageSourcePrivateDataParseRelPath(ctxt, src) < 0) return -1;
+ if (qemuStorageSourcePrivateDataParsePR(ctxt, src) < 0) + return -1; + return 0; }
@@ -1979,6 +2040,9 @@ qemuStorageSourcePrivateDataFormat(virStorageSourcePtr src, if (virStorageSourcePrivateDataFormatRelPath(src, buf) < 0) return -1;
+ if (qemuStorageSourcePrivateDataFormatPR(src, buf) < 0) + return -1; + return 0; }

On 04/13/2018 10:57 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Now that we generate pr-manager alias and socket path store them in status XML so that they are preserved across daemon restarts.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_domain.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)
So if we were to save this information (and at this point I don't think we need to), then this is where we should be allocating and filling in the private data (e.g. not in the previous patch).
How come? What would be left from the previous patch if private runtime struct would be introduced only here? Or are you just suggesting swapping these two patches?
Again as I noted previously, save the alias when printing the domain active information and I think you're good.
No, I don't want to expose info on PR helper more than is necessary. The fact that it's a separate process should not be visible to users because it is an implementation detail of QEMU. Other hypervisors might do this differently. And even though it might not be visible from the patches, using unmanaged mode is discouraged. In fact, unmanaged mode is on the edge. If pr-helper is viewed as internal implementation the unmanaged mode has no place in libvirt. However, qemu devels are experimenting with systemd socket activation and for socket path must be configurable through libvirt. So the only reason for using unmanaged PRs is systemd socket activation. Side note, we are not even exposing qemu's PID anywhere because not every hypervisor we support has VMs as separate processes.
AFAICT the only thing printed now (@relPath) is something generated via qemu_driver calls (I didn't dig deep); whereas, this data is easily regeneratable from existing domain definition data.
Yes it is. Currently. But just look at the history of channelTargetDir, e.g. a89f05ba8df095875f. We have to have qemuDomainSetPrivatePathsOld(), worse we have to keep it around for the rest of libvirt's life time. Only because nobody thought of storing channelTargerDir in runtime XML. Michal

On 04/16/2018 10:56 AM, Michal Privoznik wrote:
On 04/13/2018 10:57 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Now that we generate pr-manager alias and socket path store them in status XML so that they are preserved across daemon restarts.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_domain.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)
So if we were to save this information (and at this point I don't think we need to), then this is where we should be allocating and filling in the private data (e.g. not in the previous patch).
How come? What would be left from the previous patch if private runtime struct would be introduced only here? Or are you just suggesting swapping these two patches?
I hope I provided enough feedback in the prior response to answer this.
Again as I noted previously, save the alias when printing the domain active information and I think you're good.
No, I don't want to expose info on PR helper more than is necessary. The fact that it's a separate process should not be visible to users because it is an implementation detail of QEMU. Other hypervisors might do this differently. And even though it might not be visible from the patches, using unmanaged mode is discouraged. In fact, unmanaged mode is on the edge. If pr-helper is viewed as internal implementation the unmanaged mode has no place in libvirt. However, qemu devels are experimenting with systemd socket activation and for socket path must be configurable through libvirt. So the only reason for using unmanaged PRs is systemd socket activation.
We "expose" aliases a lot in the active domain XML. Someone that's going to add a <reservations enabled='yes' managed='yes'/> to their <disk...> definition I cannot believe would be surprised to see an alias printed. How would they know from the alias that it's a separate process? The only way to correlate the two would be to read the code and know what QEMU did to make libvirt do a little dance in order to support. As for systemd, oh great another area to fall flat on our faces... Wasn't another reason to shorten the path w/ domain name because there was some sort of bad systemd interaction?
Side note, we are not even exposing qemu's PID anywhere because not every hypervisor we support has VMs as separate processes.
The PID though could be an unexposed domain private data, couldn't it? John
AFAICT the only thing printed now (@relPath) is something generated via qemu_driver calls (I didn't dig deep); whereas, this data is easily regeneratable from existing domain definition data.
Yes it is. Currently. But just look at the history of channelTargetDir, e.g. a89f05ba8df095875f. We have to have qemuDomainSetPrivatePathsOld(), worse we have to keep it around for the rest of libvirt's life time. Only because nobody thought of storing channelTargerDir in runtime XML.
Michal

On 04/17/2018 02:00 PM, John Ferlan wrote:
On 04/16/2018 10:56 AM, Michal Privoznik wrote:
On 04/13/2018 10:57 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Now that we generate pr-manager alias and socket path store them in status XML so that they are preserved across daemon restarts.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_domain.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)
So if we were to save this information (and at this point I don't think we need to), then this is where we should be allocating and filling in the private data (e.g. not in the previous patch).
How come? What would be left from the previous patch if private runtime struct would be introduced only here? Or are you just suggesting swapping these two patches?
I hope I provided enough feedback in the prior response to answer this.
Again as I noted previously, save the alias when printing the domain active information and I think you're good.
No, I don't want to expose info on PR helper more than is necessary. The fact that it's a separate process should not be visible to users because it is an implementation detail of QEMU. Other hypervisors might do this differently. And even though it might not be visible from the patches, using unmanaged mode is discouraged. In fact, unmanaged mode is on the edge. If pr-helper is viewed as internal implementation the unmanaged mode has no place in libvirt. However, qemu devels are experimenting with systemd socket activation and for socket path must be configurable through libvirt. So the only reason for using unmanaged PRs is systemd socket activation.
We "expose" aliases a lot in the active domain XML. Someone that's going to add a <reservations enabled='yes' managed='yes'/> to their <disk...> definition I cannot believe would be surprised to see an alias printed.
We already don't expose some aliases. For instance, if a domain is configured to use hugepages and we use memory-backend-file we don't report generated aliases anywhere. Why? Because the fact we are using memory-backend-file to tell qemu to use hugepages is internal implementation. And users should not be concerned with that. It is the same story with pr-manager and its alias. It is internal implementation deatail and as such we should not expose it.
How would they know from the alias that it's a separate process? The only way to correlate the two would be to read the code and know what QEMU did to make libvirt do a little dance in order to support.
You probably misunderstood what I meant. My idea is to expose as little info back to user as possible in this case. I don't see any compelling reason for user to learn the pr-manager's alias.
As for systemd, oh great another area to fall flat on our faces... Wasn't another reason to shorten the path w/ domain name because there was some sort of bad systemd interaction?
Don't recall. It's not relevant.
Side note, we are not even exposing qemu's PID anywhere because not every hypervisor we support has VMs as separate processes.
The PID though could be an unexposed domain private data, couldn't it?
Why should we track PID of pr-helper? What do we need it for? As Peter pointed out in review to my previous patches, PIDs change therefore if we start pr-helper process with PID X, later when shutting down domain we could find a different process under the same PID. Because pr-helper might have died, released the PID and another process could have been started with the same PID. Michal

On 04/17/2018 10:19 AM, Michal Privoznik wrote:
On 04/17/2018 02:00 PM, John Ferlan wrote:
On 04/16/2018 10:56 AM, Michal Privoznik wrote:
On 04/13/2018 10:57 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Now that we generate pr-manager alias and socket path store them in status XML so that they are preserved across daemon restarts.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_domain.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)
So if we were to save this information (and at this point I don't think we need to), then this is where we should be allocating and filling in the private data (e.g. not in the previous patch).
How come? What would be left from the previous patch if private runtime struct would be introduced only here? Or are you just suggesting swapping these two patches?
I hope I provided enough feedback in the prior response to answer this.
Again as I noted previously, save the alias when printing the domain active information and I think you're good.
No, I don't want to expose info on PR helper more than is necessary. The fact that it's a separate process should not be visible to users because it is an implementation detail of QEMU. Other hypervisors might do this differently. And even though it might not be visible from the patches, using unmanaged mode is discouraged. In fact, unmanaged mode is on the edge. If pr-helper is viewed as internal implementation the unmanaged mode has no place in libvirt. However, qemu devels are experimenting with systemd socket activation and for socket path must be configurable through libvirt. So the only reason for using unmanaged PRs is systemd socket activation.
We "expose" aliases a lot in the active domain XML. Someone that's going to add a <reservations enabled='yes' managed='yes'/> to their <disk...> definition I cannot believe would be surprised to see an alias printed.
We already don't expose some aliases. For instance, if a domain is configured to use hugepages and we use memory-backend-file we don't report generated aliases anywhere. Why? Because the fact we are using memory-backend-file to tell qemu to use hugepages is internal implementation. And users should not be concerned with that. It is the same story with pr-manager and its alias. It is internal implementation deatail and as such we should not expose it.
Does that code save the alias in some private structure? The correlation being it's the libvirt <-> qemu interaction and saving it has implications related to what any future change may need to handle.
How would they know from the alias that it's a separate process? The only way to correlate the two would be to read the code and know what QEMU did to make libvirt do a little dance in order to support.
You probably misunderstood what I meant. My idea is to expose as little info back to user as possible in this case. I don't see any compelling reason for user to learn the pr-manager's alias.
As for systemd, oh great another area to fall flat on our faces... Wasn't another reason to shorten the path w/ domain name because there was some sort of bad systemd interaction?
Don't recall. It's not relevant.
Side note, we are not even exposing qemu's PID anywhere because not every hypervisor we support has VMs as separate processes.
The PID though could be an unexposed domain private data, couldn't it?
Why should we track PID of pr-helper? What do we need it for? As Peter pointed out in review to my previous patches, PIDs change therefore if we start pr-helper process with PID X, later when shutting down domain we could find a different process under the same PID. Because pr-helper might have died, released the PID and another process could have been started with the same PID.
True... Of course death of pr-helper is another problem not entirely managed by any of this IIRC - the assumption being that if we started one once, then it'll run forever, but if it does die then we won't because we assume that once started is always started. Still, as you pointed out elsewhere some sort of future event should help us to perform a restart. Leaving libvirt to manage the qemu problem. I guess I see pr-helper as a domain private thing ready to start/stop when some other disk source code comes along and says I have this thing and need to use the managed domain pr-helper - please add me. The domain pr-helper code then could say, well this is a first - something needs to be started too. Using return values you could know failure=-1, new=0, just-add=1. Keeping track of the number of disks using it in the domain private structure and when hotunplug causes that count to go to zero, we can remove the pr-helper. A hotplug or for a first time afterwards would need to start the pr [again] (and I suppose consider the namespace and cgroup implications). It's just a different way of looking at it. I find the multiple travels through ndisks and the usage of storage source private as unnecessary, but it's part of your design model so I also understand your reluctance to change that. John

On 04/17/2018 05:07 PM, John Ferlan wrote:
On 04/17/2018 10:19 AM, Michal Privoznik wrote:
On 04/17/2018 02:00 PM, John Ferlan wrote:
On 04/16/2018 10:56 AM, Michal Privoznik wrote:
On 04/13/2018 10:57 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Now that we generate pr-manager alias and socket path store them in status XML so that they are preserved across daemon restarts.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_domain.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)
So if we were to save this information (and at this point I don't think we need to), then this is where we should be allocating and filling in the private data (e.g. not in the previous patch).
How come? What would be left from the previous patch if private runtime struct would be introduced only here? Or are you just suggesting swapping these two patches?
I hope I provided enough feedback in the prior response to answer this.
Again as I noted previously, save the alias when printing the domain active information and I think you're good.
No, I don't want to expose info on PR helper more than is necessary. The fact that it's a separate process should not be visible to users because it is an implementation detail of QEMU. Other hypervisors might do this differently. And even though it might not be visible from the patches, using unmanaged mode is discouraged. In fact, unmanaged mode is on the edge. If pr-helper is viewed as internal implementation the unmanaged mode has no place in libvirt. However, qemu devels are experimenting with systemd socket activation and for socket path must be configurable through libvirt. So the only reason for using unmanaged PRs is systemd socket activation.
We "expose" aliases a lot in the active domain XML. Someone that's going to add a <reservations enabled='yes' managed='yes'/> to their <disk...> definition I cannot believe would be surprised to see an alias printed.
We already don't expose some aliases. For instance, if a domain is configured to use hugepages and we use memory-backend-file we don't report generated aliases anywhere. Why? Because the fact we are using memory-backend-file to tell qemu to use hugepages is internal implementation. And users should not be concerned with that. It is the same story with pr-manager and its alias. It is internal implementation deatail and as such we should not expose it.
Does that code save the alias in some private structure? The correlation being it's the libvirt <-> qemu interaction and saving it has implications related to what any future change may need to handle.
No it doesn't. Because that devices can't be manipulated. So the alias is constructed just once, added to the command line and the forgot. But with pr-manger we need to be able to manipulate it.
How would they know from the alias that it's a separate process? The only way to correlate the two would be to read the code and know what QEMU did to make libvirt do a little dance in order to support.
You probably misunderstood what I meant. My idea is to expose as little info back to user as possible in this case. I don't see any compelling reason for user to learn the pr-manager's alias.
As for systemd, oh great another area to fall flat on our faces... Wasn't another reason to shorten the path w/ domain name because there was some sort of bad systemd interaction?
Don't recall. It's not relevant.
Side note, we are not even exposing qemu's PID anywhere because not every hypervisor we support has VMs as separate processes.
The PID though could be an unexposed domain private data, couldn't it?
Why should we track PID of pr-helper? What do we need it for? As Peter pointed out in review to my previous patches, PIDs change therefore if we start pr-helper process with PID X, later when shutting down domain we could find a different process under the same PID. Because pr-helper might have died, released the PID and another process could have been started with the same PID.
True... Of course death of pr-helper is another problem not entirely managed by any of this IIRC - the assumption being that if we started one once, then it'll run forever, but if it does die then we won't because we assume that once started is always started. Still, as you pointed out elsewhere some sort of future event should help us to perform a restart. Leaving libvirt to manage the qemu problem.
I guess I see pr-helper as a domain private thing ready to start/stop when some other disk source code comes along and says I have this thing and need to use the managed domain pr-helper - please add me. The domain pr-helper code then could say, well this is a first - something needs to be started too. Using return values you could know failure=-1, new=0, just-add=1. Keeping track of the number of disks using it in the domain private structure and when hotunplug causes that count to go to zero, we can remove the pr-helper. A hotplug or for a first time afterwards would need to start the pr [again] (and I suppose consider the namespace and cgroup implications).
It's just a different way of looking at it. I find the multiple travels through ndisks and the usage of storage source private as unnecessary, but it's part of your design model so I also understand your reluctance to change that.
Okay, whatever. I will send another version. I give up. It will use static alias and not save anything into status XML. If it helps me to get this in so be it. Michal

If qemu-pr-helper is compiled with multipath support the first thing it does is opening /dev/mapper/control. Since we're going to be running it inside qemu namespace we need to create it there. Unfortunately, we don't know if it was compiled with or without multipath so we have to create it anyway. BTW: This might be the ugliest piece of code I've ever written but @devMapperControl really needs to be type of char * otherwise some crazy check in VIR_APPEND_ELEMENT fails. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_domain.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 0856f04406..6fe4eb57e1 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -108,6 +108,7 @@ VIR_ENUM_IMPL(qemuDomainNamespace, QEMU_DOMAIN_NS_LAST, #define PROC_MOUNTS "/proc/mounts" #define DEVPREFIX "/dev/" #define DEV_VFIO "/dev/vfio/vfio" +#define DEVICE_MAPPER_CONTROL_PATH "/dev/mapper/control" struct _qemuDomainLogContext { @@ -10269,6 +10270,11 @@ qemuDomainSetupDisk(virQEMUDriverConfigPtr cfg ATTRIBUTE_UNUSED, goto cleanup; } + /* qemu-pr-helper might require access to /dev/mapper/control. */ + if (virStoragePRDefIsEnabled(disk->src->pr) && + qemuDomainCreateDevice(DEVICE_MAPPER_CONTROL_PATH, data, true) < 0) + goto cleanup; + ret = 0; cleanup: VIR_FREE(dst); @@ -11281,6 +11287,9 @@ qemuDomainNamespaceSetupDisk(virDomainObjPtr vm, const char **paths = NULL; size_t npaths = 0; int ret = -1; + /* This is very nasty but we need it to work around some + * stupid checks in VIR_APPEND_ELEMENT macro. */ + char *devMapperControl = (char *) DEVICE_MAPPER_CONTROL_PATH; if (!qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT)) return 0; @@ -11296,6 +11305,11 @@ qemuDomainNamespaceSetupDisk(virDomainObjPtr vm, goto cleanup; } + /* qemu-pr-helper might require access to /dev/mapper/control. */ + if (virStoragePRDefIsEnabled(src->pr) && + VIR_APPEND_ELEMENT_COPY(paths, npaths, devMapperControl) < 0) + goto cleanup; + if (qemuDomainNamespaceMknodPaths(vm, paths, npaths) < 0) return -1; -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
If qemu-pr-helper is compiled with multipath support the first thing it does is opening /dev/mapper/control. Since we're going
s/opening/open
to be running it inside qemu namespace we need to create it there. Unfortunately, we don't know if it was compiled with or without multipath so we have to create it anyway.
Not sure this explains whether it's multipath or namespaces that's the focus/cause of this patch. I thought multipath allows multiple ways to address the same LUN and namespace provides a mechanism to "control" device events and protections so that something like udev doesn't change something behind our back. For PR it's a mechanism to allow certain ioctl calls to be able to control ownership and/or write processing to the LUN so that it's not possible to have two writers. So does this patch add /dev/mapper/control to the namespace list because that's what will "control" the ioctl's to the LUN used by PR ???
BTW: This might be the ugliest piece of code I've ever written but @devMapperControl really needs to be type of char * otherwise some crazy check in VIR_APPEND_ELEMENT fails.
This hunk probably doesn't belong in a commit message... and well doesn't necessarily provide the confidence level for acceptance ;-)!
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_domain.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 0856f04406..6fe4eb57e1 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -108,6 +108,7 @@ VIR_ENUM_IMPL(qemuDomainNamespace, QEMU_DOMAIN_NS_LAST, #define PROC_MOUNTS "/proc/mounts" #define DEVPREFIX "/dev/" #define DEV_VFIO "/dev/vfio/vfio" +#define DEVICE_MAPPER_CONTROL_PATH "/dev/mapper/control"
struct _qemuDomainLogContext { @@ -10269,6 +10270,11 @@ qemuDomainSetupDisk(virQEMUDriverConfigPtr cfg ATTRIBUTE_UNUSED, goto cleanup; }
+ /* qemu-pr-helper might require access to /dev/mapper/control. */ + if (virStoragePRDefIsEnabled(disk->src->pr) && + qemuDomainCreateDevice(DEVICE_MAPPER_CONTROL_PATH, data, true) < 0) + goto cleanup; + ret = 0; cleanup: VIR_FREE(dst); @@ -11281,6 +11287,9 @@ qemuDomainNamespaceSetupDisk(virDomainObjPtr vm, const char **paths = NULL; size_t npaths = 0; int ret = -1; + /* This is very nasty but we need it to work around some + * stupid checks in VIR_APPEND_ELEMENT macro. */ + char *devMapperControl = (char *) DEVICE_MAPPER_CONTROL_PATH;
alternatively char *dmPath = NULL;
if (!qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT)) return 0; @@ -11296,6 +11305,11 @@ qemuDomainNamespaceSetupDisk(virDomainObjPtr vm, goto cleanup; }
+ /* qemu-pr-helper might require access to /dev/mapper/control. */ + if (virStoragePRDefIsEnabled(src->pr) && + VIR_APPEND_ELEMENT_COPY(paths, npaths, devMapperControl) < 0)
Alternatively: VIR_STRDUP(dmPath, DEVICE_MAPPER_CONTROL_PATH) < 0) && VIR_APPEND_ELEMENT_COPY(paths, npaths, dmPath)
+ goto cleanup; + if (qemuDomainNamespaceMknodPaths(vm, paths, npaths) < 0) return -1;
BTW: Existing, but this leaks @paths
VIR_FREE(dmPath); John

On 04/14/2018 02:15 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
If qemu-pr-helper is compiled with multipath support the first thing it does is opening /dev/mapper/control. Since we're going
s/opening/open
to be running it inside qemu namespace we need to create it there. Unfortunately, we don't know if it was compiled with or without multipath so we have to create it anyway.
Not sure this explains whether it's multipath or namespaces that's the focus/cause of this patch. I thought multipath allows multiple ways to address the same LUN and namespace provides a mechanism to "control" device events and protections so that something like udev doesn't change something behind our back. For PR it's a mechanism to allow certain ioctl calls to be able to control ownership and/or write processing to the LUN so that it's not possible to have two writers> So does this patch add /dev/mapper/control to the namespace list because that's what will "control" the ioctl's to the LUN used by PR ???
No. It's because if compiled with multipath support, qemu-pr-helper uses /dev/mapper/control to query devmapper version (dm_init()): https://git.qemu.org/?p=qemu.git;a=blob;f=scsi/qemu-pr-helper.c;h=d0f83176e1... /dev/mapper/control is used to talk to devmapper (kernel) - in this particular case to check if given device is a multipath target, not to manipulate individual devices. Because if a device is multipath target then lowlevel stuff looks different (see do_pr_in() and do_pr_out()). Anyway, it shouldn't matter what pr-helper needs /dev/mapper/control for. It does so we need to create it in the namespace and allow the helper to access it.
BTW: This might be the ugliest piece of code I've ever written but @devMapperControl really needs to be type of char * otherwise some crazy check in VIR_APPEND_ELEMENT fails.
This hunk probably doesn't belong in a commit message... and well doesn't necessarily provide the confidence level for acceptance ;-)!
It's not because of allowing /dev/mapper/control but because of typecast from const char * to char *.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_domain.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 0856f04406..6fe4eb57e1 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -108,6 +108,7 @@ VIR_ENUM_IMPL(qemuDomainNamespace, QEMU_DOMAIN_NS_LAST, #define PROC_MOUNTS "/proc/mounts" #define DEVPREFIX "/dev/" #define DEV_VFIO "/dev/vfio/vfio" +#define DEVICE_MAPPER_CONTROL_PATH "/dev/mapper/control"
struct _qemuDomainLogContext { @@ -10269,6 +10270,11 @@ qemuDomainSetupDisk(virQEMUDriverConfigPtr cfg ATTRIBUTE_UNUSED, goto cleanup; }
+ /* qemu-pr-helper might require access to /dev/mapper/control. */ + if (virStoragePRDefIsEnabled(disk->src->pr) && + qemuDomainCreateDevice(DEVICE_MAPPER_CONTROL_PATH, data, true) < 0) + goto cleanup; + ret = 0; cleanup: VIR_FREE(dst); @@ -11281,6 +11287,9 @@ qemuDomainNamespaceSetupDisk(virDomainObjPtr vm, const char **paths = NULL; size_t npaths = 0; int ret = -1; + /* This is very nasty but we need it to work around some + * stupid checks in VIR_APPEND_ELEMENT macro. */ + char *devMapperControl = (char *) DEVICE_MAPPER_CONTROL_PATH;
alternatively
char *dmPath = NULL;
if (!qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT)) return 0; @@ -11296,6 +11305,11 @@ qemuDomainNamespaceSetupDisk(virDomainObjPtr vm, goto cleanup; }
+ /* qemu-pr-helper might require access to /dev/mapper/control. */ + if (virStoragePRDefIsEnabled(src->pr) && + VIR_APPEND_ELEMENT_COPY(paths, npaths, devMapperControl) < 0)
Alternatively:
VIR_STRDUP(dmPath, DEVICE_MAPPER_CONTROL_PATH) < 0) && VIR_APPEND_ELEMENT_COPY(paths, npaths, dmPath)
Okay, I'll do this. It's cleaner. Michal

Just like in previous commit, qemu-pr-helper might want to open /dev/mapper/control under certain circumstances. Therefore we have to allow it in cgroups. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_cgroup.c | 33 ++++++++++++++++++++++++++++++--- src/util/virdevmapper.c | 8 +++++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index d88eb7881f..546a4c8e63 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -114,6 +114,8 @@ qemuSetupImagePathCgroup(virDomainObjPtr vm, } +#define DEVICE_MAPPER_CONTROL_PATH "/dev/mapper/control" + static int qemuSetupImageCgroupInternal(virDomainObjPtr vm, virStorageSourcePtr src, @@ -125,6 +127,10 @@ qemuSetupImageCgroupInternal(virDomainObjPtr vm, return 0; } + if (virStoragePRDefIsManaged(src->pr) && + qemuSetupImagePathCgroup(vm, DEVICE_MAPPER_CONTROL_PATH, false) < 0) + return -1; + return qemuSetupImagePathCgroup(vm, src->path, src->readonly || forceReadonly); } @@ -142,9 +148,8 @@ qemuTeardownImageCgroup(virDomainObjPtr vm, virStorageSourcePtr src) { qemuDomainObjPrivatePtr priv = vm->privateData; - int perms = VIR_CGROUP_DEVICE_READ | - VIR_CGROUP_DEVICE_WRITE | - VIR_CGROUP_DEVICE_MKNOD; + int perms = VIR_CGROUP_DEVICE_RWM; + size_t i; int ret; if (!virCgroupHasController(priv->cgroup, @@ -157,6 +162,28 @@ qemuTeardownImageCgroup(virDomainObjPtr vm, return 0; } + for (i = 0; i < vm->def->ndisks; i++) { + virStorageSourcePtr diskSrc = vm->def->disks[i]->src; + + if (src == diskSrc) + continue; + + if (virStoragePRDefIsManaged(diskSrc->pr)) + break; + } + + if (i == vm->def->ndisks) { + VIR_DEBUG("Disabling device mapper control"); + ret = virCgroupDenyDevicePath(priv->cgroup, + DEVICE_MAPPER_CONTROL_PATH, perms, true); + virDomainAuditCgroupPath(vm, priv->cgroup, "deny", + DEVICE_MAPPER_CONTROL_PATH, + virCgroupGetDevicePermsString(perms), ret); + if (ret < 0) + return ret; + } + + VIR_DEBUG("Deny path %s", src->path); ret = virCgroupDenyDevicePath(priv->cgroup, src->path, perms, true); diff --git a/src/util/virdevmapper.c b/src/util/virdevmapper.c index d2c25af003..ef4b1e480a 100644 --- a/src/util/virdevmapper.c +++ b/src/util/virdevmapper.c @@ -101,8 +101,14 @@ virDevMapperGetTargetsImpl(const char *path, dm_task_no_open_count(dmt); - if (!dm_task_run(dmt)) + if (!dm_task_run(dmt)) { + if (errno == ENXIO) { + /* In some cases devmapper realizes this late device + * is not managed by it. */ + ret = 0; + } goto cleanup; + } if (!dm_task_get_info(dmt, &info)) goto cleanup; -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Just like in previous commit, qemu-pr-helper might want to open /dev/mapper/control under certain circumstances. Therefore we have to allow it in cgroups.
Perhaps the two patches should be combined then... yes, I know cgroups is different than namespace, so I understand the separation.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_cgroup.c | 33 ++++++++++++++++++++++++++++++--- src/util/virdevmapper.c | 8 +++++++- 2 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index d88eb7881f..546a4c8e63 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -114,6 +114,8 @@ qemuSetupImagePathCgroup(virDomainObjPtr vm, }
+#define DEVICE_MAPPER_CONTROL_PATH "/dev/mapper/control" +
Almost too bad we didn't have a common place for this in some existing included .h file.
static int qemuSetupImageCgroupInternal(virDomainObjPtr vm, virStorageSourcePtr src, @@ -125,6 +127,10 @@ qemuSetupImageCgroupInternal(virDomainObjPtr vm, return 0; }
+ if (virStoragePRDefIsManaged(src->pr) && + qemuSetupImagePathCgroup(vm, DEVICE_MAPPER_CONTROL_PATH, false) < 0) + return -1; +
Could the above be done potentially many times? Could be expensive, no? Considering what virDevMapperGetTargets[Impl] does...
return qemuSetupImagePathCgroup(vm, src->path, src->readonly || forceReadonly); }
@@ -142,9 +148,8 @@ qemuTeardownImageCgroup(virDomainObjPtr vm, virStorageSourcePtr src) { qemuDomainObjPrivatePtr priv = vm->privateData; - int perms = VIR_CGROUP_DEVICE_READ | - VIR_CGROUP_DEVICE_WRITE | - VIR_CGROUP_DEVICE_MKNOD; + int perms = VIR_CGROUP_DEVICE_RWM; + size_t i; int ret;
if (!virCgroupHasController(priv->cgroup, @@ -157,6 +162,28 @@ qemuTeardownImageCgroup(virDomainObjPtr vm, return 0; }
+ for (i = 0; i < vm->def->ndisks; i++) { + virStorageSourcePtr diskSrc = vm->def->disks[i]->src; + + if (src == diskSrc) + continue; + + if (virStoragePRDefIsManaged(diskSrc->pr)) + break; + } + + if (i == vm->def->ndisks) {
If there was only one that's managed and it's @src, then we don't get here...
+ VIR_DEBUG("Disabling device mapper control"); + ret = virCgroupDenyDevicePath(priv->cgroup, + DEVICE_MAPPER_CONTROL_PATH, perms, true);
You go through great lengths to ensure this is only done once converse to the qemuSetupImageCgroupInternal change... I guess I would think that if we can determine at Prepare time that NS and cgroups would need to add /dev/mapper/control and we only want to do that config once, then we should be able Yes, of course we then we potentially miss adding for hotplug. Honestly it seems more of a "global" to qemu_domain rather than "local" to a particular disk source even though it's needed by the local disk source, is it really something the disk source itself (or it's private data structure) should be managing?
+ virDomainAuditCgroupPath(vm, priv->cgroup, "deny", + DEVICE_MAPPER_CONTROL_PATH, + virCgroupGetDevicePermsString(perms), ret); + if (ret < 0) + return ret; + } + + VIR_DEBUG("Deny path %s", src->path);
ret = virCgroupDenyDevicePath(priv->cgroup, src->path, perms, true); diff --git a/src/util/virdevmapper.c b/src/util/virdevmapper.c index d2c25af003..ef4b1e480a 100644 --- a/src/util/virdevmapper.c +++ b/src/util/virdevmapper.c @@ -101,8 +101,14 @@ virDevMapperGetTargetsImpl(const char *path,
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt)) + if (!dm_task_run(dmt)) { + if (errno == ENXIO) { + /* In some cases devmapper realizes this late device + * is not managed by it. */ + ret = 0; + }
Should this be separated? Or is it related to /dev/mapper/control? John
goto cleanup; + }
if (!dm_task_get_info(dmt, &info)) goto cleanup;

On 04/14/2018 02:55 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Just like in previous commit, qemu-pr-helper might want to open /dev/mapper/control under certain circumstances. Therefore we have to allow it in cgroups.
Perhaps the two patches should be combined then... yes, I know cgroups is different than namespace, so I understand the separation.
Yeah, I'd like to keep them separated. Even though they allow access to the same path they do that in different areas of the code.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_cgroup.c | 33 ++++++++++++++++++++++++++++++--- src/util/virdevmapper.c | 8 +++++++- 2 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index d88eb7881f..546a4c8e63 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -114,6 +114,8 @@ qemuSetupImagePathCgroup(virDomainObjPtr vm, }
+#define DEVICE_MAPPER_CONTROL_PATH "/dev/mapper/control" +
Almost too bad we didn't have a common place for this in some existing included .h file.
Yeah :(
static int qemuSetupImageCgroupInternal(virDomainObjPtr vm, virStorageSourcePtr src, @@ -125,6 +127,10 @@ qemuSetupImageCgroupInternal(virDomainObjPtr vm, return 0; }
+ if (virStoragePRDefIsManaged(src->pr) && + qemuSetupImagePathCgroup(vm, DEVICE_MAPPER_CONTROL_PATH, false) < 0) + return -1; +
Could the above be done potentially many times? Could be expensive, no? Considering what virDevMapperGetTargets[Impl] does...
It shouldn't be expensive. At the very first call of virDevMapperGetTargetsImpl() we get ENXIO (this the change below) and thus there only very small time penalty added.
return qemuSetupImagePathCgroup(vm, src->path, src->readonly || forceReadonly); }
@@ -142,9 +148,8 @@ qemuTeardownImageCgroup(virDomainObjPtr vm, virStorageSourcePtr src) { qemuDomainObjPrivatePtr priv = vm->privateData; - int perms = VIR_CGROUP_DEVICE_READ | - VIR_CGROUP_DEVICE_WRITE | - VIR_CGROUP_DEVICE_MKNOD; + int perms = VIR_CGROUP_DEVICE_RWM; + size_t i; int ret;
if (!virCgroupHasController(priv->cgroup, @@ -157,6 +162,28 @@ qemuTeardownImageCgroup(virDomainObjPtr vm, return 0; }
+ for (i = 0; i < vm->def->ndisks; i++) { + virStorageSourcePtr diskSrc = vm->def->disks[i]->src; + + if (src == diskSrc) + continue; + + if (virStoragePRDefIsManaged(diskSrc->pr)) + break; + } + + if (i == vm->def->ndisks) {
If there was only one that's managed and it's @src, then we don't get here...
How come? Say there are 3 disks for a domain and the first one is managed: disks = {A, B, C} (where A.managed = yes}. So the loop goes like this: qemuTeardownImageCgroup(A): i = 0; if (A.src == disks[0].src) //true continue; i = 1; if (A.src == disks[1].src) //false continue; if (isManaged(disks[1]) //false break; i = 2; if (A.src == disks[2].src) //false continue; if (isManaged(disks[2]) //false break; // Here, i == ndisks == 3; Or am I missing something?
+ VIR_DEBUG("Disabling device mapper control"); + ret = virCgroupDenyDevicePath(priv->cgroup, + DEVICE_MAPPER_CONTROL_PATH, perms, true);
You go through great lengths to ensure this is only done once converse to the qemuSetupImageCgroupInternal change...
Yes, because we can't deny helper the access if there's still another disk that might be using it (note that only managed disks use helper which runs in qemu's namespace/cgroup context). But we can allow it multiple times.
I guess I would think that if we can determine at Prepare time that NS and cgroups would need to add /dev/mapper/control and we only want to do that config once, then we should be able
Yes, of course we then we potentially miss adding for hotplug. Honestly it seems more of a "global" to qemu_domain rather than "local" to a particular disk source even though it's needed by the local disk source, is it really something the disk source itself (or it's private data structure) should be managing?
Well, I view qemuSetupImageCgroup() and qemuTeardownImageCgroup() as entry points to CGroup mgmt. Therefore it's less lines of code to do decision there. Doing it anywhere else would be impractical IMO.
+ virDomainAuditCgroupPath(vm, priv->cgroup, "deny", + DEVICE_MAPPER_CONTROL_PATH, + virCgroupGetDevicePermsString(perms), ret); + if (ret < 0) + return ret; + } + + VIR_DEBUG("Deny path %s", src->path);
ret = virCgroupDenyDevicePath(priv->cgroup, src->path, perms, true); diff --git a/src/util/virdevmapper.c b/src/util/virdevmapper.c index d2c25af003..ef4b1e480a 100644 --- a/src/util/virdevmapper.c +++ b/src/util/virdevmapper.c @@ -101,8 +101,14 @@ virDevMapperGetTargetsImpl(const char *path,
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt)) + if (!dm_task_run(dmt)) { + if (errno == ENXIO) { + /* In some cases devmapper realizes this late device + * is not managed by it. */ + ret = 0; + }
Should this be separated? Or is it related to /dev/mapper/control?
No, this is exactly at the right spot in the correct patch. If virDevMapperGetTargetsImpl() sees /dev/mapper/control the dm_task_run() returns ENXIO. Speaking of which, this is not documented anywhere. I had to dig through devmapper sources to find out this errno is in fact returned by kernel. Michal

On 04/16/2018 10:56 AM, Michal Privoznik wrote:
On 04/14/2018 02:55 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Just like in previous commit, qemu-pr-helper might want to open /dev/mapper/control under certain circumstances. Therefore we have to allow it in cgroups.
Perhaps the two patches should be combined then... yes, I know cgroups is different than namespace, so I understand the separation.
Yeah, I'd like to keep them separated. Even though they allow access to the same path they do that in different areas of the code.
Separate is fine...
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_cgroup.c | 33 ++++++++++++++++++++++++++++++--- src/util/virdevmapper.c | 8 +++++++- 2 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index d88eb7881f..546a4c8e63 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -114,6 +114,8 @@ qemuSetupImagePathCgroup(virDomainObjPtr vm, }
+#define DEVICE_MAPPER_CONTROL_PATH "/dev/mapper/control" +
Almost too bad we didn't have a common place for this in some existing included .h file.
Yeah :(
static int qemuSetupImageCgroupInternal(virDomainObjPtr vm, virStorageSourcePtr src, @@ -125,6 +127,10 @@ qemuSetupImageCgroupInternal(virDomainObjPtr vm, return 0; }
+ if (virStoragePRDefIsManaged(src->pr) && + qemuSetupImagePathCgroup(vm, DEVICE_MAPPER_CONTROL_PATH, false) < 0) + return -1; +
Could the above be done potentially many times? Could be expensive, no? Considering what virDevMapperGetTargets[Impl] does...
It shouldn't be expensive. At the very first call of virDevMapperGetTargetsImpl() we get ENXIO (this the change below) and thus there only very small time penalty added.
Suffice to say it wasn't obvious to me WTF virDevMapperGetTargets[Impl] causes the ENXIO. That logic is not entirely self documenting.
return qemuSetupImagePathCgroup(vm, src->path, src->readonly || forceReadonly); }
@@ -142,9 +148,8 @@ qemuTeardownImageCgroup(virDomainObjPtr vm, virStorageSourcePtr src) { qemuDomainObjPrivatePtr priv = vm->privateData; - int perms = VIR_CGROUP_DEVICE_READ | - VIR_CGROUP_DEVICE_WRITE | - VIR_CGROUP_DEVICE_MKNOD; + int perms = VIR_CGROUP_DEVICE_RWM; + size_t i; int ret;
if (!virCgroupHasController(priv->cgroup, @@ -157,6 +162,28 @@ qemuTeardownImageCgroup(virDomainObjPtr vm, return 0; }
+ for (i = 0; i < vm->def->ndisks; i++) { + virStorageSourcePtr diskSrc = vm->def->disks[i]->src; + + if (src == diskSrc) + continue; + + if (virStoragePRDefIsManaged(diskSrc->pr)) + break; + } + + if (i == vm->def->ndisks) {
If there was only one that's managed and it's @src, then we don't get here...
How come? Say there are 3 disks for a domain and the first one is managed: disks = {A, B, C} (where A.managed = yes}.
So it made sense when I wrote the comment... let's see.
So the loop goes like this:
qemuTeardownImageCgroup(A): i = 0; if (A.src == disks[0].src) //true continue;
i = 1; if (A.src == disks[1].src) //false continue;
if (isManaged(disks[1]) //false break;
i = 2; if (A.src == disks[2].src) //false continue;
if (isManaged(disks[2]) //false break;
// Here, i == ndisks == 3;
Or am I missing something?
OK - with the example I see... /me wonders at this point whether it'd be clearer to keep a list of pr-helper managed LUN's and "Add" and "Remove" where the 1st add and the last remove trigger the PR start/stop rather than looping through ndisks.
+ VIR_DEBUG("Disabling device mapper control"); + ret = virCgroupDenyDevicePath(priv->cgroup, + DEVICE_MAPPER_CONTROL_PATH, perms, true);
You go through great lengths to ensure this is only done once converse to the qemuSetupImageCgroupInternal change...
Yes, because we can't deny helper the access if there's still another disk that might be using it (note that only managed disks use helper which runs in qemu's namespace/cgroup context). But we can allow it multiple times.
I guess I would think that if we can determine at Prepare time that NS and cgroups would need to add /dev/mapper/control and we only want to do that config once, then we should be able
Yes, of course we then we potentially miss adding for hotplug. Honestly it seems more of a "global" to qemu_domain rather than "local" to a particular disk source even though it's needed by the local disk source, is it really something the disk source itself (or it's private data structure) should be managing?
Well, I view qemuSetupImageCgroup() and qemuTeardownImageCgroup() as entry points to CGroup mgmt. Therefore it's less lines of code to do decision there. Doing it anywhere else would be impractical IMO.
+ virDomainAuditCgroupPath(vm, priv->cgroup, "deny", + DEVICE_MAPPER_CONTROL_PATH, + virCgroupGetDevicePermsString(perms), ret); + if (ret < 0) + return ret; + } + + VIR_DEBUG("Deny path %s", src->path);
ret = virCgroupDenyDevicePath(priv->cgroup, src->path, perms, true); diff --git a/src/util/virdevmapper.c b/src/util/virdevmapper.c index d2c25af003..ef4b1e480a 100644 --- a/src/util/virdevmapper.c +++ b/src/util/virdevmapper.c @@ -101,8 +101,14 @@ virDevMapperGetTargetsImpl(const char *path,
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt)) + if (!dm_task_run(dmt)) { + if (errno == ENXIO) { + /* In some cases devmapper realizes this late device + * is not managed by it. */ + ret = 0; + }
Should this be separated? Or is it related to /dev/mapper/control?
No, this is exactly at the right spot in the correct patch. If virDevMapperGetTargetsImpl() sees /dev/mapper/control the dm_task_run() returns ENXIO. Speaking of which, this is not documented anywhere. I had to dig through devmapper sources to find out this errno is in fact returned by kernel.
hmm... my self documenting code comment ;-)... The relationship between ENXIO and having /dev/mapper/control be "managed" in/by the name space code was not that obvious to me. This is stuff that'll drive someone nuts in the future when they wonder WTF is going on and why ENXIO was magically chosen and added to a cgroup related patch. John

On 04/17/2018 02:25 PM, John Ferlan wrote:
On 04/16/2018 10:56 AM, Michal Privoznik wrote:
On 04/14/2018 02:55 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Just like in previous commit, qemu-pr-helper might want to open /dev/mapper/control under certain circumstances. Therefore we have to allow it in cgroups.
Perhaps the two patches should be combined then... yes, I know cgroups is different than namespace, so I understand the separation.
Yeah, I'd like to keep them separated. Even though they allow access to the same path they do that in different areas of the code.
Separate is fine...
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_cgroup.c | 33 ++++++++++++++++++++++++++++++--- src/util/virdevmapper.c | 8 +++++++- 2 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index d88eb7881f..546a4c8e63 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -114,6 +114,8 @@ qemuSetupImagePathCgroup(virDomainObjPtr vm, }
+#define DEVICE_MAPPER_CONTROL_PATH "/dev/mapper/control" +
Almost too bad we didn't have a common place for this in some existing included .h file.
Yeah :(
static int qemuSetupImageCgroupInternal(virDomainObjPtr vm, virStorageSourcePtr src, @@ -125,6 +127,10 @@ qemuSetupImageCgroupInternal(virDomainObjPtr vm, return 0; }
+ if (virStoragePRDefIsManaged(src->pr) && + qemuSetupImagePathCgroup(vm, DEVICE_MAPPER_CONTROL_PATH, false) < 0) + return -1; +
Could the above be done potentially many times? Could be expensive, no? Considering what virDevMapperGetTargets[Impl] does...
It shouldn't be expensive. At the very first call of virDevMapperGetTargetsImpl() we get ENXIO (this the change below) and thus there only very small time penalty added.
Suffice to say it wasn't obvious to me WTF virDevMapperGetTargets[Impl] causes the ENXIO. That logic is not entirely self documenting.
return qemuSetupImagePathCgroup(vm, src->path, src->readonly || forceReadonly); }
@@ -142,9 +148,8 @@ qemuTeardownImageCgroup(virDomainObjPtr vm, virStorageSourcePtr src) { qemuDomainObjPrivatePtr priv = vm->privateData; - int perms = VIR_CGROUP_DEVICE_READ | - VIR_CGROUP_DEVICE_WRITE | - VIR_CGROUP_DEVICE_MKNOD; + int perms = VIR_CGROUP_DEVICE_RWM; + size_t i; int ret;
if (!virCgroupHasController(priv->cgroup, @@ -157,6 +162,28 @@ qemuTeardownImageCgroup(virDomainObjPtr vm, return 0; }
+ for (i = 0; i < vm->def->ndisks; i++) { + virStorageSourcePtr diskSrc = vm->def->disks[i]->src; + + if (src == diskSrc) + continue; + + if (virStoragePRDefIsManaged(diskSrc->pr)) + break; + } + + if (i == vm->def->ndisks) {
If there was only one that's managed and it's @src, then we don't get here...
How come? Say there are 3 disks for a domain and the first one is managed: disks = {A, B, C} (where A.managed = yes}.
So it made sense when I wrote the comment... let's see.
So the loop goes like this:
qemuTeardownImageCgroup(A): i = 0; if (A.src == disks[0].src) //true continue;
i = 1; if (A.src == disks[1].src) //false continue;
if (isManaged(disks[1]) //false break;
i = 2; if (A.src == disks[2].src) //false continue;
if (isManaged(disks[2]) //false break;
// Here, i == ndisks == 3;
Or am I missing something?
OK - with the example I see... /me wonders at this point whether it'd be clearer to keep a list of pr-helper managed LUN's and "Add" and "Remove" where the 1st add and the last remove trigger the PR start/stop rather than looping through ndisks.
Well, that would possibly allocate much more than 11 bytes ;-) What we can do, however, when qemu introduces the even support is to track if pr-helper is running (say, we'd have a bool under vm->privateData) and doing the following: if (vm->privateData->prRunning == false && virStoragePRDefIsManaged()) startPRHelper(); Hm.. in fact we can do something like that now even though it will not reflect reality 100%. But it's not going to be any worse that what we have now. Nor better though. If I will have to send yet another version, I might rework it. But as I say, it doesn't make any difference now. Michal

This is the easier part. All we need to do here is put -object pr-manager-helper,id=$alias,path=$socketPath and then just reference the object in -drive file.pr-manager=$alias. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_command.c | 94 ++++++++++++++++++++++ src/qemu/qemu_command.h | 3 + .../disk-virtio-scsi-reservations.args | 35 ++++++++ tests/qemuxml2argvtest.c | 4 + 4 files changed, 136 insertions(+) create mode 100644 tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 514c3ab2ef..81f6025207 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1514,6 +1514,20 @@ qemuDiskSourceGetProps(virStorageSourcePtr src) } +static void +qemuBuildDriveSourcePR(virBufferPtr buf, + virStorageSourcePtr src) +{ + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); + qemuDomainDiskPRDPtr prd = srcPriv->prd; + + if (!prd || !prd->alias) + return; + + virBufferAsprintf(buf, ",file.pr-manager=%s", prd->alias); +} + + static int qemuBuildDriveSourceStr(virDomainDiskDefPtr disk, virQEMUCapsPtr qemuCaps, @@ -1591,6 +1605,8 @@ qemuBuildDriveSourceStr(virDomainDiskDefPtr disk, if (disk->src->debug) virBufferAsprintf(buf, ",file.debug=%d", disk->src->debugLevel); + + qemuBuildDriveSourcePR(buf, disk->src); } else { if (!(source = virQEMUBuildDriveCommandlineFromJSON(srcprops))) goto cleanup; @@ -9872,6 +9888,81 @@ qemuBuildPanicCommandLine(virCommandPtr cmd, } +/** + * qemuBuildPRManagerInfoProps: + * @prd: disk PR runtime info + * @propsret: JSON properties to return + * + * Build the JSON properties for the pr-manager object. + * + * Returns: 0 on success (@propsret is NULL if no properties are needed), + * -1 on failure (with error message set). + */ +int +qemuBuildPRManagerInfoProps(qemuDomainDiskPRDPtr prd, + virJSONValuePtr *propsret) +{ + *propsret = NULL; + + if (!prd || !prd->alias) + return 0; + + if (virJSONValueObjectCreate(propsret, + "s:path", prd->path, + NULL) < 0) + return -1; + + return 0; +} + + +static int +qemuBuildMasterPRCommandLine(virCommandPtr cmd, + const virDomainDef *def) +{ + size_t i; + bool managedAdded = false; + virJSONValuePtr props = NULL; + char *tmp = NULL; + int ret = -1; + + for (i = 0; i < def->ndisks; i++) { + const virDomainDiskDef *disk = def->disks[i]; + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + qemuDomainDiskPRDPtr prd = srcPriv->prd; + + + if (virStoragePRDefIsManaged(disk->src->pr)) { + if (managedAdded) + continue; + + managedAdded = true; + } + + if (qemuBuildPRManagerInfoProps(prd, &props) < 0) + goto cleanup; + + if (!props) + continue; + + if (!(tmp = virQEMUBuildObjectCommandlineFromJSON("pr-manager-helper", + prd->alias, + props))) + goto cleanup; + virJSONValueFree(props); + props = NULL; + + virCommandAddArgList(cmd, "-object", tmp, NULL); + VIR_FREE(tmp); + } + + ret = 0; + cleanup: + virJSONValueFree(props); + return ret; +} + + /** * qemuBuildCommandLineValidate: * @@ -10024,6 +10115,9 @@ qemuBuildCommandLine(virQEMUDriverPtr driver, if (qemuBuildMasterKeyCommandLine(cmd, priv) < 0) goto error; + if (qemuBuildMasterPRCommandLine(cmd, def) < 0) + goto error; + if (enableFips) virCommandAddArg(cmd, "-enable-fips"); diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 31c9da673c..32f3697181 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -54,6 +54,9 @@ virCommandPtr qemuBuildCommandLine(virQEMUDriverPtr driver, size_t *nnicindexes, int **nicindexes); +/* Generate the object properties for pr-manager */ +int qemuBuildPRManagerInfoProps(qemuDomainDiskPRDPtr prd, + virJSONValuePtr *propsret); /* Generate the object properties for a secret */ int qemuBuildSecretInfoProps(qemuDomainSecretInfoPtr secinfo, diff --git a/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args b/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args new file mode 100644 index 0000000000..195e83a048 --- /dev/null +++ b/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args @@ -0,0 +1,35 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-system-i686 \ +-name QEMUGuest1 \ +-S \ +-object pr-manager-helper,id=pr-helper0,\ +path=/tmp/lib/domain--1-QEMUGuest1/pr-helper0.sock \ +-object pr-manager-helper,id=pr-helper-scsi0-0-0-1,\ +path=/path/to/qemu-pr-helper.sock \ +-M pc \ +-m 214 \ +-smp 8,sockets=8,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,\ +server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=readline \ +-no-acpi \ +-boot c \ +-device virtio-scsi-pci,id=scsi0,num_queues=8,bus=pci.0,addr=0x3 \ +-usb \ +-drive file=/dev/HostVG/QEMUGuest1,file.pr-manager=pr-helper0,format=raw,\ +if=none,id=drive-scsi0-0-0-0,boot=on \ +-device scsi-block,bus=scsi0.0,channel=0,scsi-id=0,lun=0,\ +drive=drive-scsi0-0-0-0,id=scsi0-0-0-0 \ +-drive file=/dev/HostVG/QEMUGuest2,file.pr-manager=pr-helper-scsi0-0-0-1,\ +format=raw,if=none,id=drive-scsi0-0-0-1 \ +-device scsi-block,bus=scsi0.0,channel=0,scsi-id=0,lun=1,\ +drive=drive-scsi0-0-0-1,id=scsi0-0-0-1 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 165137e93c..fdaeb473d0 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -3055,6 +3055,10 @@ mymain(void) QEMU_CAPS_PIIX_DISABLE_S3, QEMU_CAPS_PIIX_DISABLE_S4, QEMU_CAPS_ICH9_USB_EHCI1, QEMU_CAPS_PCI_MULTIFUNCTION); + DO_TEST("disk-virtio-scsi-reservations", + QEMU_CAPS_DRIVE_BOOT, QEMU_CAPS_VIRTIO_SCSI, + QEMU_CAPS_SCSI_BLOCK, QEMU_CAPS_PR_MANAGER_HELPER); + /* Test disks with format probing enabled for legacy reasons. * New tests should not go in this section. */ driver.config->allowDiskFormatProbing = true; -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
This is the easier part. All we need to do here is put -object pr-manager-helper,id=$alias,path=$socketPath and then just reference the object in -drive file.pr-manager=$alias.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_command.c | 94 ++++++++++++++++++++++ src/qemu/qemu_command.h | 3 + .../disk-virtio-scsi-reservations.args | 35 ++++++++ tests/qemuxml2argvtest.c | 4 + 4 files changed, 136 insertions(+) create mode 100644 tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args
This patch fails qemuxml2argv for me because of commits 'cc32731a' and 'a32539de'... 1430) QEMU XML-2-ARGV disk-virtio-scsi-reservations ... In '/home/jferlan/git/libvirt.work/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args': Offset 405 Expect [defaults -chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,server,nowait -mon chardev=charmonitor,id=monitor,mode=readline] Actual [-user-config -nodefaults -chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,server,nowait -mon chardev=charmonitor,id=monitor,mode=control] ... FAILED Simple fix... regenerating the output gets: diff --git a/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args b/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args index 195e83a048..0bdd35a18a 100644 --- a/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args +++ b/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args @@ -16,10 +16,11 @@ path=/path/to/qemu-pr-helper.sock \ -smp 8,sockets=8,cores=1,threads=1 \ -uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ -nographic \ +-no-user-config \ -nodefaults \ -chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,\ server,nowait \ --mon chardev=charmonitor,id=monitor,mode=readline \ +-mon chardev=charmonitor,id=monitor,mode=control \ -no-acpi \ -boot c \ -device virtio-scsi-pci,id=scsi0,num_queues=8,bus=pci.0,addr=0x3 \
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 514c3ab2ef..81f6025207 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1514,6 +1514,20 @@ qemuDiskSourceGetProps(virStorageSourcePtr src) }
+static void +qemuBuildDriveSourcePR(virBufferPtr buf, + virStorageSourcePtr src) +{ + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); + qemuDomainDiskPRDPtr prd = srcPriv->prd; + + if (!prd || !prd->alias) + return; + + virBufferAsprintf(buf, ",file.pr-manager=%s", prd->alias); +} + + static int qemuBuildDriveSourceStr(virDomainDiskDefPtr disk, virQEMUCapsPtr qemuCaps, @@ -1591,6 +1605,8 @@ qemuBuildDriveSourceStr(virDomainDiskDefPtr disk,
if (disk->src->debug) virBufferAsprintf(buf, ",file.debug=%d", disk->src->debugLevel); + + qemuBuildDriveSourcePR(buf, disk->src); } else { if (!(source = virQEMUBuildDriveCommandlineFromJSON(srcprops))) goto cleanup; @@ -9872,6 +9888,81 @@ qemuBuildPanicCommandLine(virCommandPtr cmd, }
+/** + * qemuBuildPRManagerInfoProps: + * @prd: disk PR runtime info + * @propsret: JSON properties to return + * + * Build the JSON properties for the pr-manager object. + * + * Returns: 0 on success (@propsret is NULL if no properties are needed), + * -1 on failure (with error message set). + */ +int +qemuBuildPRManagerInfoProps(qemuDomainDiskPRDPtr prd, + virJSONValuePtr *propsret) +{ + *propsret = NULL; + + if (!prd || !prd->alias) + return 0; + + if (virJSONValueObjectCreate(propsret, + "s:path", prd->path, + NULL) < 0) + return -1; + + return 0; +} + + +static int +qemuBuildMasterPRCommandLine(virCommandPtr cmd, + const virDomainDef *def) +{ + size_t i; + bool managedAdded = false; + virJSONValuePtr props = NULL; + char *tmp = NULL; + int ret = -1; + + for (i = 0; i < def->ndisks; i++) { + const virDomainDiskDef *disk = def->disks[i]; + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + qemuDomainDiskPRDPtr prd = srcPriv->prd; + + + if (virStoragePRDefIsManaged(disk->src->pr)) { + if (managedAdded) + continue; + + managedAdded = true;
As soon as we find one that needs managing we should break from the loop and then only add pr-manager-helper if we have one to manage. No sense going through the rest of the list of disks. Move @disk into the top level and then use it to decide whether or not to add the object. Then in some way signify that the object was added so that future hotplugs wouldn't need to somehow guess - a simple check that it was added already would seem to suffice.
+ }
The next hunk would seem to be useful for the hotplug path, no? Including perhaps setting some sort of qemu_domain level boolean that the object was added already so that subsequent or consecutive hotplugs wouldn't try to add it again. John
+ + if (qemuBuildPRManagerInfoProps(prd, &props) < 0) + goto cleanup; + + if (!props) + continue; + + if (!(tmp = virQEMUBuildObjectCommandlineFromJSON("pr-manager-helper", + prd->alias, + props))) + goto cleanup; + virJSONValueFree(props); + props = NULL; + + virCommandAddArgList(cmd, "-object", tmp, NULL); + VIR_FREE(tmp); + } + + ret = 0; + cleanup: + virJSONValueFree(props); + return ret; +} + + /** * qemuBuildCommandLineValidate: * @@ -10024,6 +10115,9 @@ qemuBuildCommandLine(virQEMUDriverPtr driver, if (qemuBuildMasterKeyCommandLine(cmd, priv) < 0) goto error;
+ if (qemuBuildMasterPRCommandLine(cmd, def) < 0) + goto error; + if (enableFips) virCommandAddArg(cmd, "-enable-fips");
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 31c9da673c..32f3697181 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -54,6 +54,9 @@ virCommandPtr qemuBuildCommandLine(virQEMUDriverPtr driver, size_t *nnicindexes, int **nicindexes);
+/* Generate the object properties for pr-manager */ +int qemuBuildPRManagerInfoProps(qemuDomainDiskPRDPtr prd, + virJSONValuePtr *propsret);
/* Generate the object properties for a secret */ int qemuBuildSecretInfoProps(qemuDomainSecretInfoPtr secinfo, diff --git a/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args b/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args new file mode 100644 index 0000000000..195e83a048 --- /dev/null +++ b/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args @@ -0,0 +1,35 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-system-i686 \ +-name QEMUGuest1 \ +-S \ +-object pr-manager-helper,id=pr-helper0,\ +path=/tmp/lib/domain--1-QEMUGuest1/pr-helper0.sock \ +-object pr-manager-helper,id=pr-helper-scsi0-0-0-1,\ +path=/path/to/qemu-pr-helper.sock \ +-M pc \ +-m 214 \ +-smp 8,sockets=8,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,\ +server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=readline \ +-no-acpi \ +-boot c \ +-device virtio-scsi-pci,id=scsi0,num_queues=8,bus=pci.0,addr=0x3 \ +-usb \ +-drive file=/dev/HostVG/QEMUGuest1,file.pr-manager=pr-helper0,format=raw,\ +if=none,id=drive-scsi0-0-0-0,boot=on \ +-device scsi-block,bus=scsi0.0,channel=0,scsi-id=0,lun=0,\ +drive=drive-scsi0-0-0-0,id=scsi0-0-0-0 \ +-drive file=/dev/HostVG/QEMUGuest2,file.pr-manager=pr-helper-scsi0-0-0-1,\ +format=raw,if=none,id=drive-scsi0-0-0-1 \ +-device scsi-block,bus=scsi0.0,channel=0,scsi-id=0,lun=1,\ +drive=drive-scsi0-0-0-1,id=scsi0-0-0-1 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 165137e93c..fdaeb473d0 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -3055,6 +3055,10 @@ mymain(void) QEMU_CAPS_PIIX_DISABLE_S3, QEMU_CAPS_PIIX_DISABLE_S4, QEMU_CAPS_ICH9_USB_EHCI1, QEMU_CAPS_PCI_MULTIFUNCTION);
+ DO_TEST("disk-virtio-scsi-reservations", + QEMU_CAPS_DRIVE_BOOT, QEMU_CAPS_VIRTIO_SCSI, + QEMU_CAPS_SCSI_BLOCK, QEMU_CAPS_PR_MANAGER_HELPER); + /* Test disks with format probing enabled for legacy reasons. * New tests should not go in this section. */ driver.config->allowDiskFormatProbing = true;

On 04/14/2018 03:04 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
This is the easier part. All we need to do here is put -object pr-manager-helper,id=$alias,path=$socketPath and then just reference the object in -drive file.pr-manager=$alias.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_command.c | 94 ++++++++++++++++++++++ src/qemu/qemu_command.h | 3 + .../disk-virtio-scsi-reservations.args | 35 ++++++++ tests/qemuxml2argvtest.c | 4 + 4 files changed, 136 insertions(+) create mode 100644 tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args
This patch fails qemuxml2argv for me because of commits 'cc32731a' and 'a32539de'...
Oh yeah. There's always chance that between me posting patches and review somebody will push something that invalidates my patches.
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 514c3ab2ef..81f6025207 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1514,6 +1514,20 @@ qemuDiskSourceGetProps(virStorageSourcePtr src) }
+static void +qemuBuildDriveSourcePR(virBufferPtr buf, + virStorageSourcePtr src) +{ + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); + qemuDomainDiskPRDPtr prd = srcPriv->prd; + + if (!prd || !prd->alias) + return; + + virBufferAsprintf(buf, ",file.pr-manager=%s", prd->alias); +} + + static int qemuBuildDriveSourceStr(virDomainDiskDefPtr disk, virQEMUCapsPtr qemuCaps, @@ -1591,6 +1605,8 @@ qemuBuildDriveSourceStr(virDomainDiskDefPtr disk,
if (disk->src->debug) virBufferAsprintf(buf, ",file.debug=%d", disk->src->debugLevel); + + qemuBuildDriveSourcePR(buf, disk->src); } else { if (!(source = virQEMUBuildDriveCommandlineFromJSON(srcprops))) goto cleanup; @@ -9872,6 +9888,81 @@ qemuBuildPanicCommandLine(virCommandPtr cmd, }
+/** + * qemuBuildPRManagerInfoProps: + * @prd: disk PR runtime info + * @propsret: JSON properties to return + * + * Build the JSON properties for the pr-manager object. + * + * Returns: 0 on success (@propsret is NULL if no properties are needed), + * -1 on failure (with error message set). + */ +int +qemuBuildPRManagerInfoProps(qemuDomainDiskPRDPtr prd, + virJSONValuePtr *propsret) +{ + *propsret = NULL; + + if (!prd || !prd->alias) + return 0; + + if (virJSONValueObjectCreate(propsret, + "s:path", prd->path, + NULL) < 0) + return -1; + + return 0; +} + + +static int +qemuBuildMasterPRCommandLine(virCommandPtr cmd, + const virDomainDef *def) +{ + size_t i; + bool managedAdded = false; + virJSONValuePtr props = NULL; + char *tmp = NULL; + int ret = -1; + + for (i = 0; i < def->ndisks; i++) { + const virDomainDiskDef *disk = def->disks[i]; + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + qemuDomainDiskPRDPtr prd = srcPriv->prd; + + + if (virStoragePRDefIsManaged(disk->src->pr)) { + if (managedAdded) + continue; + + managedAdded = true;
As soon as we find one that needs managing we should break from the loop and then only add pr-manager-helper if we have one to manage. No sense going through the rest of the list of disks. Move @disk into the top level and then use it to decide whether or not to add the object. Then in some way signify that the object was added so that future hotplugs wouldn't need to somehow guess - a simple check that it was added already would seem to suffice.
Not true. We need to add manager objects even for cases where libvirt is not managing the helper process. For instance, for the following XML: <disk type='block' device='lun'> <source dev='/dev/HostVG/QEMUGuest2'> <reservations enabled='yes' managed='no'> <source type='unix' path='/path/to/qemu-pr-helper.sock' mode='client'/> </reservations> </source> </disk> the following cmd line needs to be generated: -object pr-manager-helper,id=pr-helper-scsi0-0-0-1,\ path=/path/to/qemu-pr-helper.sock \ otherwise qemu would now know where to connect. However, if libvirt is managing the pr-helper process, all the managed disks share the same process => object on the command line => it must be added exactly once.
+ }
The next hunk would seem to be useful for the hotplug path, no? Including perhaps setting some sort of qemu_domain level boolean that the object was added already so that subsequent or consecutive hotplugs wouldn't try to add it again.
It's reused there yes. As Peter suggested in one of earlier reviews instead of having two functions (one for generating cmd line and the other for JSON) I can write just one and use virQEMUBuildObjectCommandlineFromJSON() to translate JSON into cmd line. Michal

On 04/16/2018 10:56 AM, Michal Privoznik wrote:
On 04/14/2018 03:04 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
This is the easier part. All we need to do here is put -object pr-manager-helper,id=$alias,path=$socketPath and then just reference the object in -drive file.pr-manager=$alias.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_command.c | 94 ++++++++++++++++++++++ src/qemu/qemu_command.h | 3 + .../disk-virtio-scsi-reservations.args | 35 ++++++++ tests/qemuxml2argvtest.c | 4 + 4 files changed, 136 insertions(+) create mode 100644 tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args
This patch fails qemuxml2argv for me because of commits 'cc32731a' and 'a32539de'...
Oh yeah. There's always chance that between me posting patches and review somebody will push something that invalidates my patches.
True, especially with the amount/rate of change in the capabilities area! In some ways it's, point it out, let you know I ACKnowledge that you'll have some merge work, but that work isn't something that'd be a problem w/r/t the overall patch.
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 514c3ab2ef..81f6025207 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1514,6 +1514,20 @@ qemuDiskSourceGetProps(virStorageSourcePtr src) }
+static void +qemuBuildDriveSourcePR(virBufferPtr buf, + virStorageSourcePtr src) +{ + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); + qemuDomainDiskPRDPtr prd = srcPriv->prd; + + if (!prd || !prd->alias) + return; + + virBufferAsprintf(buf, ",file.pr-manager=%s", prd->alias); +} + + static int qemuBuildDriveSourceStr(virDomainDiskDefPtr disk, virQEMUCapsPtr qemuCaps, @@ -1591,6 +1605,8 @@ qemuBuildDriveSourceStr(virDomainDiskDefPtr disk,
if (disk->src->debug) virBufferAsprintf(buf, ",file.debug=%d", disk->src->debugLevel); + + qemuBuildDriveSourcePR(buf, disk->src); } else { if (!(source = virQEMUBuildDriveCommandlineFromJSON(srcprops))) goto cleanup; @@ -9872,6 +9888,81 @@ qemuBuildPanicCommandLine(virCommandPtr cmd, }
+/** + * qemuBuildPRManagerInfoProps: + * @prd: disk PR runtime info + * @propsret: JSON properties to return + * + * Build the JSON properties for the pr-manager object. + * + * Returns: 0 on success (@propsret is NULL if no properties are needed), + * -1 on failure (with error message set). + */ +int +qemuBuildPRManagerInfoProps(qemuDomainDiskPRDPtr prd, + virJSONValuePtr *propsret) +{ + *propsret = NULL; + + if (!prd || !prd->alias) + return 0; + + if (virJSONValueObjectCreate(propsret, + "s:path", prd->path, + NULL) < 0) + return -1; + + return 0; +} + + +static int +qemuBuildMasterPRCommandLine(virCommandPtr cmd, + const virDomainDef *def) +{ + size_t i; + bool managedAdded = false; + virJSONValuePtr props = NULL; + char *tmp = NULL; + int ret = -1; + + for (i = 0; i < def->ndisks; i++) { + const virDomainDiskDef *disk = def->disks[i]; + qemuDomainStorageSourcePrivatePtr srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + qemuDomainDiskPRDPtr prd = srcPriv->prd; + + + if (virStoragePRDefIsManaged(disk->src->pr)) { + if (managedAdded) + continue; + + managedAdded = true;
As soon as we find one that needs managing we should break from the loop and then only add pr-manager-helper if we have one to manage. No sense going through the rest of the list of disks. Move @disk into the top level and then use it to decide whether or not to add the object. Then in some way signify that the object was added so that future hotplugs wouldn't need to somehow guess - a simple check that it was added already would seem to suffice.
Not true. We need to add manager objects even for cases where libvirt is not managing the helper process. For instance, for the following XML:
<disk type='block' device='lun'> <source dev='/dev/HostVG/QEMUGuest2'> <reservations enabled='yes' managed='no'> <source type='unix' path='/path/to/qemu-pr-helper.sock' mode='client'/> </reservations> </source> </disk>
the following cmd line needs to be generated:
-object pr-manager-helper,id=pr-helper-scsi0-0-0-1,\ path=/path/to/qemu-pr-helper.sock \
otherwise qemu would now know where to connect. However, if libvirt is managing the pr-helper process, all the managed disks share the same process => object on the command line => it must be added exactly once.
oh, right... "pr-manager-helper" is the object name, <sigh>
+ }
The next hunk would seem to be useful for the hotplug path, no? Including perhaps setting some sort of qemu_domain level boolean that the object was added already so that subsequent or consecutive hotplugs wouldn't try to add it again.
It's reused there yes. As Peter suggested in one of earlier reviews instead of having two functions (one for generating cmd line and the other for JSON) I can write just one and use virQEMUBuildObjectCommandlineFromJSON() to translate JSON into cmd line.
OK - I had scanned that review and had that comment in mind, but didn't correlate the two when going through this (nor go back and check here once I looked at hotplug later). John

On 04/16/2018 04:56 PM, Michal Privoznik wrote:
On 04/14/2018 03:04 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
This is the easier part. All we need to do here is put -object pr-manager-helper,id=$alias,path=$socketPath and then just reference the object in -drive file.pr-manager=$alias.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_command.c | 94 ++++++++++++++++++++++ src/qemu/qemu_command.h | 3 + .../disk-virtio-scsi-reservations.args | 35 ++++++++ tests/qemuxml2argvtest.c | 4 + 4 files changed, 136 insertions(+) create mode 100644 tests/qemuxml2argvdata/disk-virtio-scsi-reservations.args
So just to give everybody an idea what is this patch going to look like, I'm attaching it here. I'd like to ask if this is really way we want to go. Frankly, I find the patch ugly, because in order to not strdup() a static string, I have to differentiate every time I need an alias. Just look at qemuBuildDriveSourcePR(). I hate it so much, that I made qemuBuildPRManagerInfoProps() to stdup() the string. Just think how it would look like if I did no such thing. Michal

Just like we allow users overriding path to bridge-helper detected at compile time we can allow them to override path to qemu-pr-helper. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- m4/virt-driver-qemu.m4 | 5 +++++ src/qemu/libvirtd_qemu.aug | 1 + src/qemu/qemu.conf | 4 ++++ src/qemu/qemu_conf.c | 7 ++++++- src/qemu/qemu_conf.h | 1 + src/qemu/test_libvirtd_qemu.aug.in | 1 + 6 files changed, 18 insertions(+), 1 deletion(-) diff --git a/m4/virt-driver-qemu.m4 b/m4/virt-driver-qemu.m4 index b9bafdab90..80e1d3ad46 100644 --- a/m4/virt-driver-qemu.m4 +++ b/m4/virt-driver-qemu.m4 @@ -57,6 +57,11 @@ AC_DEFUN([LIBVIRT_DRIVER_CHECK_QEMU], [ [/usr/libexec:/usr/lib/qemu:/usr/lib]) AC_DEFINE_UNQUOTED([QEMU_BRIDGE_HELPER], ["$QEMU_BRIDGE_HELPER"], [QEMU bridge helper]) + AC_PATH_PROG([QEMU_PR_HELPER], [qemu-pr-helper], + [/usr/bin/qemu-pr-helper], + [/usr/bin:/usr/libexec]) + AC_DEFINE_UNQUOTED([QEMU_PR_HELPER], ["$QEMU_PR_HELPER"], + [QEMU PR helper]) ]) AC_DEFUN([LIBVIRT_DRIVER_RESULT_QEMU], [ diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug index c19bf3a43a..2dc16e91fd 100644 --- a/src/qemu/libvirtd_qemu.aug +++ b/src/qemu/libvirtd_qemu.aug @@ -86,6 +86,7 @@ module Libvirtd_qemu = let process_entry = str_entry "hugetlbfs_mount" | bool_entry "clear_emulator_capabilities" | str_entry "bridge_helper" + | str_entry "pr_helper" | bool_entry "set_process_name" | int_entry "max_processes" | int_entry "max_files" diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 07eab7efff..30fdd54e2c 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -775,3 +775,7 @@ # This directory is used for memoryBacking source if configured as file. # NOTE: big files will be stored here #memory_backing_dir = "/var/lib/libvirt/qemu/ram" + +# Path to the SCSI persistent reservations helper. This helper is +# used whenever <reservations/> are enabled for SCSI disks. +#pr_helper = "/usr/libexec/qemu-pr-helper" diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 36cf3a281c..8c69dbe75c 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -307,7 +307,8 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged) goto error; } - if (VIR_STRDUP(cfg->bridgeHelperName, QEMU_BRIDGE_HELPER) < 0) + if (VIR_STRDUP(cfg->bridgeHelperName, QEMU_BRIDGE_HELPER) < 0 || + VIR_STRDUP(cfg->prHelperName, QEMU_PR_HELPER) < 0) goto error; cfg->clearEmulatorCapabilities = true; @@ -392,6 +393,7 @@ static void virQEMUDriverConfigDispose(void *obj) } VIR_FREE(cfg->hugetlbfs); VIR_FREE(cfg->bridgeHelperName); + VIR_FREE(cfg->prHelperName); VIR_FREE(cfg->saveImageFormat); VIR_FREE(cfg->dumpImageFormat); @@ -759,6 +761,9 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, if (virConfGetValueString(conf, "bridge_helper", &cfg->bridgeHelperName) < 0) goto cleanup; + if (virConfGetValueString(conf, "pr_helper", &cfg->prHelperName) < 0) + goto cleanup; + if (virConfGetValueBool(conf, "mac_filter", &cfg->macFilter) < 0) goto cleanup; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index e1ad5463f3..7a63780c48 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -153,6 +153,7 @@ struct _virQEMUDriverConfig { size_t nhugetlbfs; char *bridgeHelperName; + char *prHelperName; bool macFilter; diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in index 688e5b9fda..c0efae47bd 100644 --- a/src/qemu/test_libvirtd_qemu.aug.in +++ b/src/qemu/test_libvirtd_qemu.aug.in @@ -100,3 +100,4 @@ module Test_libvirtd_qemu = { "1" = "mount" } } { "memory_backing_dir" = "/var/lib/libvirt/qemu/ram" } +{ "pr_helper" = "/usr/libexec/qemu-pr-helper" } -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Just like we allow users overriding path to bridge-helper detected at compile time we can allow them to override path to qemu-pr-helper.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- m4/virt-driver-qemu.m4 | 5 +++++ src/qemu/libvirtd_qemu.aug | 1 + src/qemu/qemu.conf | 4 ++++ src/qemu/qemu_conf.c | 7 ++++++- src/qemu/qemu_conf.h | 1 + src/qemu/test_libvirtd_qemu.aug.in | 1 + 6 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/m4/virt-driver-qemu.m4 b/m4/virt-driver-qemu.m4 index b9bafdab90..80e1d3ad46 100644 --- a/m4/virt-driver-qemu.m4 +++ b/m4/virt-driver-qemu.m4 @@ -57,6 +57,11 @@ AC_DEFUN([LIBVIRT_DRIVER_CHECK_QEMU], [ [/usr/libexec:/usr/lib/qemu:/usr/lib]) AC_DEFINE_UNQUOTED([QEMU_BRIDGE_HELPER], ["$QEMU_BRIDGE_HELPER"], [QEMU bridge helper]) + AC_PATH_PROG([QEMU_PR_HELPER], [qemu-pr-helper], + [/usr/bin/qemu-pr-helper], + [/usr/bin:/usr/libexec])
So the default install location of qemu-pr-helper is /usr/bin unlike bridge-helper which is /usr/libexec?
+ AC_DEFINE_UNQUOTED([QEMU_PR_HELPER], ["$QEMU_PR_HELPER"], + [QEMU PR helper]) ])
AC_DEFUN([LIBVIRT_DRIVER_RESULT_QEMU], [ diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug index c19bf3a43a..2dc16e91fd 100644 --- a/src/qemu/libvirtd_qemu.aug +++ b/src/qemu/libvirtd_qemu.aug @@ -86,6 +86,7 @@ module Libvirtd_qemu = let process_entry = str_entry "hugetlbfs_mount" | bool_entry "clear_emulator_capabilities" | str_entry "bridge_helper" + | str_entry "pr_helper" | bool_entry "set_process_name" | int_entry "max_processes" | int_entry "max_files" diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 07eab7efff..30fdd54e2c 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -775,3 +775,7 @@ # This directory is used for memoryBacking source if configured as file. # NOTE: big files will be stored here #memory_backing_dir = "/var/lib/libvirt/qemu/ram" + +# Path to the SCSI persistent reservations helper. This helper is +# used whenever <reservations/> are enabled for SCSI disks.
s/disks/LUN devices/
+#pr_helper = "/usr/libexec/qemu-pr-helper"
Going with my note above - the default path in the m4 file shows /usr/bin first... So should this match that? Similar to how qemu-bridge-helper matches the /usr/libexec install location?
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 36cf3a281c..8c69dbe75c 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -307,7 +307,8 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged) goto error; }
- if (VIR_STRDUP(cfg->bridgeHelperName, QEMU_BRIDGE_HELPER) < 0) + if (VIR_STRDUP(cfg->bridgeHelperName, QEMU_BRIDGE_HELPER) < 0 || + VIR_STRDUP(cfg->prHelperName, QEMU_PR_HELPER) < 0) goto error;
cfg->clearEmulatorCapabilities = true; @@ -392,6 +393,7 @@ static void virQEMUDriverConfigDispose(void *obj) } VIR_FREE(cfg->hugetlbfs); VIR_FREE(cfg->bridgeHelperName); + VIR_FREE(cfg->prHelperName);
VIR_FREE(cfg->saveImageFormat); VIR_FREE(cfg->dumpImageFormat); @@ -759,6 +761,9 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, if (virConfGetValueString(conf, "bridge_helper", &cfg->bridgeHelperName) < 0) goto cleanup;
+ if (virConfGetValueString(conf, "pr_helper", &cfg->prHelperName) < 0) + goto cleanup; + if (virConfGetValueBool(conf, "mac_filter", &cfg->macFilter) < 0) goto cleanup;
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index e1ad5463f3..7a63780c48 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -153,6 +153,7 @@ struct _virQEMUDriverConfig { size_t nhugetlbfs;
char *bridgeHelperName; + char *prHelperName;
bool macFilter;
diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in index 688e5b9fda..c0efae47bd 100644 --- a/src/qemu/test_libvirtd_qemu.aug.in +++ b/src/qemu/test_libvirtd_qemu.aug.in @@ -100,3 +100,4 @@ module Test_libvirtd_qemu = { "1" = "mount" } } { "memory_backing_dir" = "/var/lib/libvirt/qemu/ram" } +{ "pr_helper" = "/usr/libexec/qemu-pr-helper" }
And don't forget this one if something does change, but I'm sure that'd wash out of the check syntax-check I think the questions/issues are minimal and easily fixed by a response, so consider the concept at least Reviewed-by: John Ferlan <jferlan@redhat.com> John

On 04/14/2018 03:35 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Just like we allow users overriding path to bridge-helper detected at compile time we can allow them to override path to qemu-pr-helper.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- m4/virt-driver-qemu.m4 | 5 +++++ src/qemu/libvirtd_qemu.aug | 1 + src/qemu/qemu.conf | 4 ++++ src/qemu/qemu_conf.c | 7 ++++++- src/qemu/qemu_conf.h | 1 + src/qemu/test_libvirtd_qemu.aug.in | 1 + 6 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/m4/virt-driver-qemu.m4 b/m4/virt-driver-qemu.m4 index b9bafdab90..80e1d3ad46 100644 --- a/m4/virt-driver-qemu.m4 +++ b/m4/virt-driver-qemu.m4 @@ -57,6 +57,11 @@ AC_DEFUN([LIBVIRT_DRIVER_CHECK_QEMU], [ [/usr/libexec:/usr/lib/qemu:/usr/lib]) AC_DEFINE_UNQUOTED([QEMU_BRIDGE_HELPER], ["$QEMU_BRIDGE_HELPER"], [QEMU bridge helper]) + AC_PATH_PROG([QEMU_PR_HELPER], [qemu-pr-helper], + [/usr/bin/qemu-pr-helper], + [/usr/bin:/usr/libexec])
So the default install location of qemu-pr-helper is /usr/bin unlike bridge-helper which is /usr/libexec?
Yes. I guess this has something to do with aforementioned systemd socket activation.
+ AC_DEFINE_UNQUOTED([QEMU_PR_HELPER], ["$QEMU_PR_HELPER"], + [QEMU PR helper]) ])
AC_DEFUN([LIBVIRT_DRIVER_RESULT_QEMU], [ diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug index c19bf3a43a..2dc16e91fd 100644 --- a/src/qemu/libvirtd_qemu.aug +++ b/src/qemu/libvirtd_qemu.aug @@ -86,6 +86,7 @@ module Libvirtd_qemu = let process_entry = str_entry "hugetlbfs_mount" | bool_entry "clear_emulator_capabilities" | str_entry "bridge_helper" + | str_entry "pr_helper" | bool_entry "set_process_name" | int_entry "max_processes" | int_entry "max_files" diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 07eab7efff..30fdd54e2c 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -775,3 +775,7 @@ # This directory is used for memoryBacking source if configured as file. # NOTE: big files will be stored here #memory_backing_dir = "/var/lib/libvirt/qemu/ram" + +# Path to the SCSI persistent reservations helper. This helper is +# used whenever <reservations/> are enabled for SCSI disks.
s/disks/LUN devices/
+#pr_helper = "/usr/libexec/qemu-pr-helper"
Going with my note above - the default path in the m4 file shows /usr/bin first... So should this match that? Similar to how qemu-bridge-helper matches the /usr/libexec install location?
Yeah, this is a leftover from previous rounds where I had /usr/libexec/. I'm changing this to /usr/bin/.
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 36cf3a281c..8c69dbe75c 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -307,7 +307,8 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged) goto error; }
- if (VIR_STRDUP(cfg->bridgeHelperName, QEMU_BRIDGE_HELPER) < 0) + if (VIR_STRDUP(cfg->bridgeHelperName, QEMU_BRIDGE_HELPER) < 0 || + VIR_STRDUP(cfg->prHelperName, QEMU_PR_HELPER) < 0) goto error;
cfg->clearEmulatorCapabilities = true; @@ -392,6 +393,7 @@ static void virQEMUDriverConfigDispose(void *obj) } VIR_FREE(cfg->hugetlbfs); VIR_FREE(cfg->bridgeHelperName); + VIR_FREE(cfg->prHelperName);
VIR_FREE(cfg->saveImageFormat); VIR_FREE(cfg->dumpImageFormat); @@ -759,6 +761,9 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, if (virConfGetValueString(conf, "bridge_helper", &cfg->bridgeHelperName) < 0) goto cleanup;
+ if (virConfGetValueString(conf, "pr_helper", &cfg->prHelperName) < 0) + goto cleanup; + if (virConfGetValueBool(conf, "mac_filter", &cfg->macFilter) < 0) goto cleanup;
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index e1ad5463f3..7a63780c48 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -153,6 +153,7 @@ struct _virQEMUDriverConfig { size_t nhugetlbfs;
char *bridgeHelperName; + char *prHelperName;
bool macFilter;
diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in index 688e5b9fda..c0efae47bd 100644 --- a/src/qemu/test_libvirtd_qemu.aug.in +++ b/src/qemu/test_libvirtd_qemu.aug.in @@ -100,3 +100,4 @@ module Test_libvirtd_qemu = { "1" = "mount" } } { "memory_backing_dir" = "/var/lib/libvirt/qemu/ram" } +{ "pr_helper" = "/usr/libexec/qemu-pr-helper" }
And don't forget this one if something does change, but I'm sure that'd wash out of the check syntax-check
I think the questions/issues are minimal and easily fixed by a response, so consider the concept at least
Reviewed-by: John Ferlan <jferlan@redhat.com>
Thanks, Michal

Before we exec() qemu we have to spawn pr-helper processes for all managed reservations (well, technically there can only one). The only caveat there is that we should place the process into the same namespace and cgroup as qemu (so that it shares the same view of the system). But we can do that only after we've forked. That means calling the setup function between fork() and exec(). Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_process.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f02114c693..982c989a0a 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2556,6 +2556,223 @@ qemuProcessResctrlCreate(virQEMUDriverPtr driver, ret = 0; cleanup: virObjectUnref(caps); + + return ret; +} + + +static char * +qemuProcessBuildPRHelperPidfilePath(virDomainObjPtr vm, + const char *prdAlias) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + return virPidFileBuildPath(priv->libDir, prdAlias); +} + + +static void +qemuProcessKillPRDaemon(virDomainObjPtr vm) +{ + virErrorPtr orig_err; + char *prAlias; + char *prPidfile; + + if (!(prAlias = qemuDomainGetManagedPRAlias())) { + VIR_WARN("Unable to get default PR manager alias"); + return; + } + + if (!(prPidfile = qemuProcessBuildPRHelperPidfilePath(vm, prAlias))) { + VIR_WARN("Unable to construct pr-helper pidfile path"); + VIR_FREE(prAlias); + return; + } + VIR_FREE(prAlias); + + virErrorPreserveLast(&orig_err); + if (virPidFileForceCleanupPath(prPidfile) < 0) { + VIR_WARN("Unable to kill pr-helper process"); + } else if (unlink(prPidfile) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("Unable to remove stale pidfile %s"), + prPidfile); + } + virErrorRestore(&orig_err); + + VIR_FREE(prPidfile); +} + + +static int +qemuProcessStartPRDaemonHook(void *opaque) +{ + virDomainObjPtr vm = opaque; + size_t i, nfds = 0; + int *fds = NULL; + int ret = -1; + + if (virProcessGetNamespaces(vm->pid, &nfds, &fds) < 0) + return ret; + + if (nfds > 0 && + virProcessSetNamespaces(nfds, fds) < 0) + goto cleanup; + + ret = 0; + cleanup: + for (i = 0; i < nfds; i++) + VIR_FORCE_CLOSE(fds[i]); + VIR_FREE(fds); + return ret; +} + + +static int +qemuProcessStartPRDaemon(virDomainObjPtr vm, + qemuDomainDiskPRDPtr prd) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + virQEMUDriverConfigPtr cfg; + int errfd = -1; + char *pidfile = NULL; + int pidfd = -1; + pid_t cpid = -1; + virCommandPtr cmd = NULL; + virTimeBackOffVar timebackoff; + const unsigned long long timeout = 500000; /* ms */ + int ret = -1; + + cfg = virQEMUDriverGetConfig(driver); + + if (!virFileIsExecutable(cfg->prHelperName)) { + virReportSystemError(errno, _("'%s' is not a suitable pr helper"), + cfg->prHelperName); + goto cleanup; + } + + if (!(pidfile = qemuProcessBuildPRHelperPidfilePath(vm, prd->alias))) + goto cleanup; + + /* Just try to acquire. Dummy pid will be replaced later */ + if ((pidfd = virPidFileAcquirePath(pidfile, false, -1)) < 0) + goto cleanup; + + /* Remove stale socket */ + if (unlink(prd->path) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("Unable to remove stale socket path: %s"), + prd->path); + goto cleanup; + } + + if (!(cmd = virCommandNewArgList(cfg->prHelperName, + "-k", prd->path, + "-f", pidfile, + NULL))) + goto cleanup; + + virCommandDaemonize(cmd); + /* We want our virCommand to write child PID into the pidfile + * so that we can read it even before exec(). */ + virCommandSetPidFile(cmd, pidfile); + virCommandSetErrorFD(cmd, &errfd); + + /* Place the process into the same namespace and cgroup as + * qemu (so that it shares the same view of the system). */ + virCommandSetPreExecHook(cmd, qemuProcessStartPRDaemonHook, vm); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + if (virPidFileReadPath(pidfile, &cpid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("pr helper %s didn't show up"), prd->alias); + goto cleanup; + } + + if (virTimeBackOffStart(&timebackoff, 1, timeout) < 0) + goto cleanup; + while (virTimeBackOffWait(&timebackoff)) { + char errbuf[1024] = { 0 }; + + if (virFileExists(prd->path)) + break; + + if (virProcessKill(cpid, 0) == 0) + continue; + + if (saferead(errfd, errbuf, sizeof(errbuf) - 1) < 0) { + virReportSystemError(errno, + _("pr helper %s died unexpectedly"), prd->alias); + } else { + virReportError(VIR_ERR_OPERATION_FAILED, + _("pr helper died and reported: %s"), errbuf); + } + goto cleanup; + } + + if (!virFileExists(prd->path)) { + virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s", + _("pr helper socked did not show up")); + goto cleanup; + } + + if (priv->cgroup && + virCgroupAddMachineTask(priv->cgroup, cpid) < 0) + goto cleanup; + + if (qemuSecurityDomainSetPathLabel(driver->securityManager, + vm->def, prd->path, true) < 0) + goto cleanup; + + ret = 0; + cleanup: + if (ret < 0) { + virCommandAbort(cmd); + if (cpid >= 0) + virProcessKillPainfully(cpid, true); + unlink(pidfile); + } + virCommandFree(cmd); + VIR_FORCE_CLOSE(pidfd); + VIR_FREE(pidfile); + VIR_FORCE_CLOSE(errfd); + virObjectUnref(cfg); + return ret; +} + + +static int +qemuProcessMaybeStartPRDaemon(virDomainObjPtr vm) +{ + size_t i; + int ret = -1; + qemuDomainDiskPRDPtr prd = NULL; + + for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *disk = vm->def->disks[i]; + qemuDomainStorageSourcePrivatePtr srcPriv; + + if (!virStoragePRDefIsManaged(disk->src->pr)) + continue; + + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + prd = srcPriv->prd; + break; + } + + if (prd && + qemuProcessStartPRDaemon(vm, prd) < 0) + goto cleanup; + + ret = 0; + cleanup: + if (ret < 0) + qemuProcessKillPRDaemon(vm); return ret; } @@ -6073,6 +6290,10 @@ qemuProcessLaunch(virConnectPtr conn, if (qemuProcessResctrlCreate(driver, vm) < 0) goto cleanup; + VIR_DEBUG("Setting up PR daemon"); + if (qemuProcessMaybeStartPRDaemon(vm) < 0) + goto cleanup; + VIR_DEBUG("Setting domain security labels"); if (qemuSecuritySetAllLabel(driver, vm, @@ -6600,6 +6821,9 @@ void qemuProcessStop(virQEMUDriverPtr driver, /* Remove the master key */ qemuDomainMasterKeyRemove(priv); + /* Do this before we delete the tree and remove pidfile. */ + qemuProcessKillPRDaemon(vm); + virFileDeleteTree(priv->libDir); virFileDeleteTree(priv->channelTargetDir); -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Before we exec() qemu we have to spawn pr-helper processes for all managed reservations (well, technically there can only one).
Don't mince words - what have to spawn the qemu-pr-helper process for all managed persistent reservations.
The only caveat there is that we should place the process into the same namespace and cgroup as qemu (so that it shares the same view of the system). But we can do that only after we've forked. That means calling the setup function between fork() and exec().
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_process.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f02114c693..982c989a0a 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2556,6 +2556,223 @@ qemuProcessResctrlCreate(virQEMUDriverPtr driver, ret = 0; cleanup: virObjectUnref(caps); + + return ret; +} + + +static char * +qemuProcessBuildPRHelperPidfilePath(virDomainObjPtr vm, + const char *prdAlias) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + return virPidFileBuildPath(priv->libDir, prdAlias); +} + + +static void +qemuProcessKillPRDaemon(virDomainObjPtr vm) +{ + virErrorPtr orig_err; + char *prAlias; + char *prPidfile;
I would hope that we'd be able to figure out in some way that we actually started this for the the domain.
+ + if (!(prAlias = qemuDomainGetManagedPRAlias())) { + VIR_WARN("Unable to get default PR manager alias"); + return; + } + + if (!(prPidfile = qemuProcessBuildPRHelperPidfilePath(vm, prAlias))) {
Since we know that we're using the managed one, then we should be able to pass/formulate the "static" prAlias here rather than possibly failing because we don't have enough memory above and then need to free it immediately afterwards.
+ VIR_WARN("Unable to construct pr-helper pidfile path"); + VIR_FREE(prAlias); + return; + } + VIR_FREE(prAlias); + + virErrorPreserveLast(&orig_err); + if (virPidFileForceCleanupPath(prPidfile) < 0) { + VIR_WARN("Unable to kill pr-helper process"); + } else if (unlink(prPidfile) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("Unable to remove stale pidfile %s"), + prPidfile); + } + virErrorRestore(&orig_err); + + VIR_FREE(prPidfile); +} + + +static int +qemuProcessStartPRDaemonHook(void *opaque) +{ + virDomainObjPtr vm = opaque; + size_t i, nfds = 0; + int *fds = NULL; + int ret = -1; + + if (virProcessGetNamespaces(vm->pid, &nfds, &fds) < 0) + return ret; + + if (nfds > 0 && + virProcessSetNamespaces(nfds, fds) < 0) + goto cleanup; + + ret = 0; + cleanup: + for (i = 0; i < nfds; i++) + VIR_FORCE_CLOSE(fds[i]); + VIR_FREE(fds); + return ret; +} + + +static int +qemuProcessStartPRDaemon(virDomainObjPtr vm, + qemuDomainDiskPRDPtr prd) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + virQEMUDriverConfigPtr cfg; + int errfd = -1; + char *pidfile = NULL; + int pidfd = -1; + pid_t cpid = -1; + virCommandPtr cmd = NULL; + virTimeBackOffVar timebackoff; + const unsigned long long timeout = 500000; /* ms */ + int ret = -1; + + cfg = virQEMUDriverGetConfig(driver); + + if (!virFileIsExecutable(cfg->prHelperName)) { + virReportSystemError(errno, _("'%s' is not a suitable pr helper"), + cfg->prHelperName); + goto cleanup; + } + + if (!(pidfile = qemuProcessBuildPRHelperPidfilePath(vm, prd->alias))) + goto cleanup; + + /* Just try to acquire. Dummy pid will be replaced later */ + if ((pidfd = virPidFileAcquirePath(pidfile, false, -1)) < 0) + goto cleanup; + + /* Remove stale socket */ + if (unlink(prd->path) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("Unable to remove stale socket path: %s"), + prd->path); + goto cleanup; + } + + if (!(cmd = virCommandNewArgList(cfg->prHelperName, + "-k", prd->path, + "-f", pidfile, + NULL))) + goto cleanup; + + virCommandDaemonize(cmd); + /* We want our virCommand to write child PID into the pidfile + * so that we can read it even before exec(). */ + virCommandSetPidFile(cmd, pidfile); + virCommandSetErrorFD(cmd, &errfd); + + /* Place the process into the same namespace and cgroup as + * qemu (so that it shares the same view of the system). */ + virCommandSetPreExecHook(cmd, qemuProcessStartPRDaemonHook, vm); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + if (virPidFileReadPath(pidfile, &cpid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("pr helper %s didn't show up"), prd->alias); + goto cleanup; + } + + if (virTimeBackOffStart(&timebackoff, 1, timeout) < 0) + goto cleanup; + while (virTimeBackOffWait(&timebackoff)) { + char errbuf[1024] = { 0 }; + + if (virFileExists(prd->path)) + break; + + if (virProcessKill(cpid, 0) == 0) + continue; + + if (saferead(errfd, errbuf, sizeof(errbuf) - 1) < 0) { + virReportSystemError(errno, + _("pr helper %s died unexpectedly"), prd->alias); + } else { + virReportError(VIR_ERR_OPERATION_FAILED, + _("pr helper died and reported: %s"), errbuf); + } + goto cleanup; + } + + if (!virFileExists(prd->path)) { + virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s", + _("pr helper socked did not show up")); + goto cleanup; + } + + if (priv->cgroup && + virCgroupAddMachineTask(priv->cgroup, cpid) < 0) + goto cleanup; + + if (qemuSecurityDomainSetPathLabel(driver->securityManager, + vm->def, prd->path, true) < 0) + goto cleanup; + + ret = 0; + cleanup: + if (ret < 0) { + virCommandAbort(cmd); + if (cpid >= 0) + virProcessKillPainfully(cpid, true); + unlink(pidfile);
Coverity lets us know we can get here w/ pidfile == NULL which isn't good for unlink.
+ } + virCommandFree(cmd); + VIR_FORCE_CLOSE(pidfd); + VIR_FREE(pidfile); + VIR_FORCE_CLOSE(errfd); + virObjectUnref(cfg); + return ret; +} + + +static int +qemuProcessMaybeStartPRDaemon(virDomainObjPtr vm) +{ + size_t i; + int ret = -1; + qemuDomainDiskPRDPtr prd = NULL; + + for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *disk = vm->def->disks[i]; + qemuDomainStorageSourcePrivatePtr srcPriv; + + if (!virStoragePRDefIsManaged(disk->src->pr)) + continue; + + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + prd = srcPriv->prd; + break; + }
Now that we know we needed to start one, then once we're successful we should be able to store that we did start it... and that could be added to some sort of private structure. This is where I could see a private data being used. John
+ + if (prd && + qemuProcessStartPRDaemon(vm, prd) < 0) + goto cleanup; + + ret = 0; + cleanup: + if (ret < 0) + qemuProcessKillPRDaemon(vm); return ret; }
@@ -6073,6 +6290,10 @@ qemuProcessLaunch(virConnectPtr conn, if (qemuProcessResctrlCreate(driver, vm) < 0) goto cleanup;
+ VIR_DEBUG("Setting up PR daemon"); + if (qemuProcessMaybeStartPRDaemon(vm) < 0) + goto cleanup; + VIR_DEBUG("Setting domain security labels"); if (qemuSecuritySetAllLabel(driver, vm, @@ -6600,6 +6821,9 @@ void qemuProcessStop(virQEMUDriverPtr driver, /* Remove the master key */ qemuDomainMasterKeyRemove(priv);
+ /* Do this before we delete the tree and remove pidfile. */ + qemuProcessKillPRDaemon(vm); + virFileDeleteTree(priv->libDir); virFileDeleteTree(priv->channelTargetDir);

On 04/14/2018 04:56 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Before we exec() qemu we have to spawn pr-helper processes for all managed reservations (well, technically there can only one).
Don't mince words - what have to spawn the qemu-pr-helper process for all managed persistent reservations.
The only caveat there is that we should place the process into the same namespace and cgroup as qemu (so that it shares the same view of the system). But we can do that only after we've forked. That means calling the setup function between fork() and exec().
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_process.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f02114c693..982c989a0a 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2556,6 +2556,223 @@ qemuProcessResctrlCreate(virQEMUDriverPtr driver, ret = 0; cleanup: virObjectUnref(caps); + + return ret; +} + + +static char * +qemuProcessBuildPRHelperPidfilePath(virDomainObjPtr vm, + const char *prdAlias) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + return virPidFileBuildPath(priv->libDir, prdAlias); +} + + +static void +qemuProcessKillPRDaemon(virDomainObjPtr vm) +{ + virErrorPtr orig_err; + char *prAlias; + char *prPidfile;
I would hope that we'd be able to figure out in some way that we actually started this for the the domain.
+ + if (!(prAlias = qemuDomainGetManagedPRAlias())) { + VIR_WARN("Unable to get default PR manager alias"); + return; + } + + if (!(prPidfile = qemuProcessBuildPRHelperPidfilePath(vm, prAlias))) {
Since we know that we're using the managed one, then we should be able to pass/formulate the "static" prAlias here rather than possibly failing because we don't have enough memory above and then need to free it immediately afterwards.
Yeah, well if there's not enough memory to allocate 11 bytes there's probably not enough memory to do any stuff done below, i.e. qemuProcessBuildPRHelperPidfilePath(), or any syscall done in virPidFileForceCleanupPath().
+ VIR_WARN("Unable to construct pr-helper pidfile path"); + VIR_FREE(prAlias); + return; + } + VIR_FREE(prAlias); + + virErrorPreserveLast(&orig_err); + if (virPidFileForceCleanupPath(prPidfile) < 0) { + VIR_WARN("Unable to kill pr-helper process"); + } else if (unlink(prPidfile) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("Unable to remove stale pidfile %s"), + prPidfile); + } + virErrorRestore(&orig_err); + + VIR_FREE(prPidfile); +} +
+static int +qemuProcessMaybeStartPRDaemon(virDomainObjPtr vm) +{ + size_t i; + int ret = -1; + qemuDomainDiskPRDPtr prd = NULL; + + for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *disk = vm->def->disks[i]; + qemuDomainStorageSourcePrivatePtr srcPriv; + + if (!virStoragePRDefIsManaged(disk->src->pr)) + continue; + + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + prd = srcPriv->prd; + break; + }
Now that we know we needed to start one, then once we're successful we should be able to store that we did start it... and that could be added to some sort of private structure. This is where I could see a private data being used.
Well, we can have that when qemu finally implements events. Then we can keep this internal state in sync with reality. Michal

On 04/16/2018 10:57 AM, Michal Privoznik wrote:
On 04/14/2018 04:56 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Before we exec() qemu we have to spawn pr-helper processes for all managed reservations (well, technically there can only one).
Don't mince words - what have to spawn the qemu-pr-helper process for all managed persistent reservations.
The only caveat there is that we should place the process into the same namespace and cgroup as qemu (so that it shares the same view of the system). But we can do that only after we've forked. That means calling the setup function between fork() and exec().
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_process.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f02114c693..982c989a0a 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2556,6 +2556,223 @@ qemuProcessResctrlCreate(virQEMUDriverPtr driver, ret = 0; cleanup: virObjectUnref(caps); + + return ret; +} + + +static char * +qemuProcessBuildPRHelperPidfilePath(virDomainObjPtr vm, + const char *prdAlias) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + return virPidFileBuildPath(priv->libDir, prdAlias); +} + + +static void +qemuProcessKillPRDaemon(virDomainObjPtr vm) +{ + virErrorPtr orig_err; + char *prAlias; + char *prPidfile;
I would hope that we'd be able to figure out in some way that we actually started this for the the domain.
+ + if (!(prAlias = qemuDomainGetManagedPRAlias())) { + VIR_WARN("Unable to get default PR manager alias"); + return; + } + + if (!(prPidfile = qemuProcessBuildPRHelperPidfilePath(vm, prAlias))) {
Since we know that we're using the managed one, then we should be able to pass/formulate the "static" prAlias here rather than possibly failing because we don't have enough memory above and then need to free it immediately afterwards.
Yeah, well if there's not enough memory to allocate 11 bytes there's probably not enough memory to do any stuff done below, i.e. qemuProcessBuildPRHelperPidfilePath(), or any syscall done in virPidFileForceCleanupPath().
True, if we're lacking memory not much is going to happen.
+ VIR_WARN("Unable to construct pr-helper pidfile path"); + VIR_FREE(prAlias); + return; + } + VIR_FREE(prAlias); + + virErrorPreserveLast(&orig_err); + if (virPidFileForceCleanupPath(prPidfile) < 0) { + VIR_WARN("Unable to kill pr-helper process"); + } else if (unlink(prPidfile) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("Unable to remove stale pidfile %s"), + prPidfile); + } + virErrorRestore(&orig_err); + + VIR_FREE(prPidfile); +} +
+static int +qemuProcessMaybeStartPRDaemon(virDomainObjPtr vm) +{ + size_t i; + int ret = -1; + qemuDomainDiskPRDPtr prd = NULL; + + for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *disk = vm->def->disks[i]; + qemuDomainStorageSourcePrivatePtr srcPriv; + + if (!virStoragePRDefIsManaged(disk->src->pr)) + continue; + + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + prd = srcPriv->prd; + break; + }
Now that we know we needed to start one, then once we're successful we should be able to store that we did start it... and that could be added to some sort of private structure. This is where I could see a private data being used.
Well, we can have that when qemu finally implements events. Then we can keep this internal state in sync with reality.
The managed pr-helper is domain level based on some storage sources needing it... That's more what I was referring to. If the domain private kept track of it being started (no matter how it did so), then as we found/added/hotplugged a storage source that needed it, then we wouldn't have to keep running the entire ndisks and checking whether something may have already added it or not. I dunno, it's I guess it's just implementation details and a different way of thinking about how to relate this bolted on piece of software. John

When plugging a disk into domain we need to start qemu-pr-helper process iff this is the first disk with PR enabled for the domain. Otherwise the helper is already running (or not needed). Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_hotplug.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_process.c | 4 ++-- src/qemu/qemu_process.h | 5 +++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index f0d549de38..468153c79c 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -348,6 +348,49 @@ qemuDomainChangeEjectableMedia(virQEMUDriverPtr driver, } +/** + * qemuDomainMaybeStartPRDaemon: + * @vm: domain object + * @disk: disk to hotplug + * + * Checks if it's needed to start qemu-pr-helper and starts it. + * + * Returns: 0 if qemu-pr-helper is not needed + * 1 if it is needed and was started + * -1 otherwise. + */ +static int +qemuDomainMaybeStartPRDaemon(virDomainObjPtr vm, + virDomainDiskDefPtr disk) +{ + size_t i; + qemuDomainStorageSourcePrivatePtr srcPriv; + + if (!virStoragePRDefIsManaged(disk->src->pr)) { + /* @disk itself does not require qemu-pr-helper. */ + return 0; + } + + for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *domainDisk = vm->def->disks[i]; + + if (virStoragePRDefIsManaged(domainDisk->src->pr)) { + /* qemu-pr-helper should be already started because + * another disk in domain requires it. */ + return 0; + } + } + + /* @disk requires qemu-pr-helper but none is running. + * Start it now. */ + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + if (qemuProcessStartPRDaemon(vm, srcPriv->prd) < 0) + return -1; + + return 1; +} + + /** * qemuDomainAttachDiskGeneric: * @@ -368,6 +411,7 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, bool driveAdded = false; bool secobjAdded = false; bool encobjAdded = false; + bool prdStarted = false; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virJSONValuePtr secobjProps = NULL; virJSONValuePtr encobjProps = NULL; @@ -384,6 +428,11 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (qemuDomainPrepareDiskSource(disk, priv, cfg) < 0) goto error; + if ((rv = qemuDomainMaybeStartPRDaemon(vm, disk)) < 0) + goto cleanup; + else if (rv > 0) + prdStarted = true; + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); if (srcPriv) { secinfo = srcPriv->secinfo; @@ -481,6 +530,8 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, error: qemuDomainDelDiskSrcTLSObject(driver, vm, disk->src); ignore_value(qemuHotplugPrepareDiskAccess(driver, vm, disk, NULL, true)); + if (prdStarted) + qemuProcessKillPRDaemon(vm); goto cleanup; } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 982c989a0a..11aaeabb38 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2571,7 +2571,7 @@ qemuProcessBuildPRHelperPidfilePath(virDomainObjPtr vm, } -static void +void qemuProcessKillPRDaemon(virDomainObjPtr vm) { virErrorPtr orig_err; @@ -2629,7 +2629,7 @@ qemuProcessStartPRDaemonHook(void *opaque) } -static int +int qemuProcessStartPRDaemon(virDomainObjPtr vm, qemuDomainDiskPRDPtr prd) { diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h index 2741115673..8df5832e23 100644 --- a/src/qemu/qemu_process.h +++ b/src/qemu/qemu_process.h @@ -203,4 +203,9 @@ int qemuProcessRefreshDisks(virQEMUDriverPtr driver, virDomainObjPtr vm, qemuDomainAsyncJob asyncJob); +int qemuProcessStartPRDaemon(virDomainObjPtr vm, + qemuDomainDiskPRDPtr prd); + +void qemuProcessKillPRDaemon(virDomainObjPtr vm); + #endif /* __QEMU_PROCESS_H__ */ -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
When plugging a disk into domain we need to start qemu-pr-helper process iff this is the first disk with PR enabled for the domain. Otherwise the helper is already running (or not needed).
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_hotplug.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_process.c | 4 ++-- src/qemu/qemu_process.h | 5 +++++ 3 files changed, 58 insertions(+), 2 deletions(-)
Personally not a fan of splitting up the hotplug need for PR daemon from need for adding the object to the disk and the separate unplug patch, but I understand why it's done this way.
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index f0d549de38..468153c79c 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -348,6 +348,49 @@ qemuDomainChangeEjectableMedia(virQEMUDriverPtr driver, }
+/** + * qemuDomainMaybeStartPRDaemon: + * @vm: domain object + * @disk: disk to hotplug + * + * Checks if it's needed to start qemu-pr-helper and starts it. + * + * Returns: 0 if qemu-pr-helper is not needed + * 1 if it is needed and was started + * -1 otherwise. + */ +static int +qemuDomainMaybeStartPRDaemon(virDomainObjPtr vm, + virDomainDiskDefPtr disk) +{ + size_t i; + qemuDomainStorageSourcePrivatePtr srcPriv; + + if (!virStoragePRDefIsManaged(disk->src->pr)) { + /* @disk itself does not require qemu-pr-helper. */ + return 0; + } +
If we used the fact that the thing was already started for the domain in some sort of domain private data (which could even store the PID-file path), then we wouldn't need the following...
+ for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *domainDisk = vm->def->disks[i]; + + if (virStoragePRDefIsManaged(domainDisk->src->pr)) { + /* qemu-pr-helper should be already started because + * another disk in domain requires it. */ + return 0; + } + } + + /* @disk requires qemu-pr-helper but none is running. + * Start it now. */ + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + if (qemuProcessStartPRDaemon(vm, srcPriv->prd) < 0) + return -1;
and the above would set things for us (conceivably) John
+ + return 1; +} + + /** * qemuDomainAttachDiskGeneric: * @@ -368,6 +411,7 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, bool driveAdded = false; bool secobjAdded = false; bool encobjAdded = false; + bool prdStarted = false; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virJSONValuePtr secobjProps = NULL; virJSONValuePtr encobjProps = NULL; @@ -384,6 +428,11 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (qemuDomainPrepareDiskSource(disk, priv, cfg) < 0) goto error;
+ if ((rv = qemuDomainMaybeStartPRDaemon(vm, disk)) < 0) + goto cleanup; + else if (rv > 0) + prdStarted = true; + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); if (srcPriv) { secinfo = srcPriv->secinfo; @@ -481,6 +530,8 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, error: qemuDomainDelDiskSrcTLSObject(driver, vm, disk->src); ignore_value(qemuHotplugPrepareDiskAccess(driver, vm, disk, NULL, true)); + if (prdStarted) + qemuProcessKillPRDaemon(vm); goto cleanup; }
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 982c989a0a..11aaeabb38 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2571,7 +2571,7 @@ qemuProcessBuildPRHelperPidfilePath(virDomainObjPtr vm, }
-static void +void qemuProcessKillPRDaemon(virDomainObjPtr vm) { virErrorPtr orig_err; @@ -2629,7 +2629,7 @@ qemuProcessStartPRDaemonHook(void *opaque) }
-static int +int qemuProcessStartPRDaemon(virDomainObjPtr vm, qemuDomainDiskPRDPtr prd) { diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h index 2741115673..8df5832e23 100644 --- a/src/qemu/qemu_process.h +++ b/src/qemu/qemu_process.h @@ -203,4 +203,9 @@ int qemuProcessRefreshDisks(virQEMUDriverPtr driver, virDomainObjPtr vm, qemuDomainAsyncJob asyncJob);
+int qemuProcessStartPRDaemon(virDomainObjPtr vm, + qemuDomainDiskPRDPtr prd); + +void qemuProcessKillPRDaemon(virDomainObjPtr vm); + #endif /* __QEMU_PROCESS_H__ */

When attaching a disk that requires pr-manager we might need to plug the pr-manager object too. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_hotplug.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 468153c79c..43bb910ed6 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -391,6 +391,29 @@ qemuDomainMaybeStartPRDaemon(virDomainObjPtr vm, } +static int +qemuMaybeBuildPRManagerInfoProps(virDomainObjPtr vm, + qemuDomainDiskPRDPtr prd, + virJSONValuePtr *propsret) +{ + size_t i; + + *propsret = NULL; + + for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *domainDisk = vm->def->disks[i]; + + if (virStoragePRDefIsManaged(domainDisk->src->pr)) { + /* qemu-pr-helper should be already started because + * another disk in domain requires it. */ + return 0; + } + } + + return qemuBuildPRManagerInfoProps(prd, propsret); +} + + /** * qemuDomainAttachDiskGeneric: * @@ -411,13 +434,16 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, bool driveAdded = false; bool secobjAdded = false; bool encobjAdded = false; + bool prmgrAdded = false; bool prdStarted = false; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virJSONValuePtr secobjProps = NULL; virJSONValuePtr encobjProps = NULL; + virJSONValuePtr prmgrProps = NULL; qemuDomainStorageSourcePrivatePtr srcPriv; qemuDomainSecretInfoPtr secinfo = NULL; qemuDomainSecretInfoPtr encinfo = NULL; + qemuDomainDiskPRDPtr prd = NULL; if (qemuHotplugPrepareDiskAccess(driver, vm, disk, NULL, false) < 0) goto cleanup; @@ -437,6 +463,7 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (srcPriv) { secinfo = srcPriv->secinfo; encinfo = srcPriv->encinfo; + prd = srcPriv->prd; } if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) { @@ -447,6 +474,9 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (encinfo && qemuBuildSecretInfoProps(encinfo, &encobjProps) < 0) goto error; + if (qemuMaybeBuildPRManagerInfoProps(vm, prd, &prmgrProps) < 0) + goto error; + if (disk->src->haveTLS && qemuDomainAddDiskSrcTLSObject(driver, vm, disk->src, disk->info.alias) < 0) @@ -484,6 +514,15 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, encobjAdded = true; } + if (prmgrProps) { + rv = qemuMonitorAddObject(priv->mon, "pr-manager-helper", prd->alias, + prmgrProps); + prmgrProps = NULL; /* qemuMonitorAddObject consumes */ + if (rv < 0) + goto exit_monitor; + prmgrAdded = true; + } + if (qemuMonitorAddDrive(priv->mon, drivestr) < 0) goto exit_monitor; driveAdded = true; @@ -521,6 +560,8 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, ignore_value(qemuMonitorDelObject(priv->mon, secinfo->s.aes.alias)); if (encobjAdded) ignore_value(qemuMonitorDelObject(priv->mon, encinfo->s.aes.alias)); + if (prmgrAdded) + ignore_value(qemuMonitorDelObject(priv->mon, prd->alias)); if (qemuDomainObjExitMonitor(driver, vm) < 0) ret = -2; virErrorRestore(&orig_err); -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
When attaching a disk that requires pr-manager we might need to plug the pr-manager object too.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_hotplug.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+)
My opinion - combine w/ previous patch.
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 468153c79c..43bb910ed6 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -391,6 +391,29 @@ qemuDomainMaybeStartPRDaemon(virDomainObjPtr vm, }
+static int +qemuMaybeBuildPRManagerInfoProps(virDomainObjPtr vm, + qemuDomainDiskPRDPtr prd, + virJSONValuePtr *propsret) +{ + size_t i; + + *propsret = NULL; + + for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *domainDisk = vm->def->disks[i]; + + if (virStoragePRDefIsManaged(domainDisk->src->pr)) { + /* qemu-pr-helper should be already started because + * another disk in domain requires it. */ + return 0; + } + }
I don't understand the need for the above hunk - that was done in patch11 wasn't it? This is why I like things combined - going back and forth and changing logic as patches are developed means something may be missed.
+ + return qemuBuildPRManagerInfoProps(prd, propsret); +} + + /** * qemuDomainAttachDiskGeneric: * @@ -411,13 +434,16 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, bool driveAdded = false; bool secobjAdded = false; bool encobjAdded = false; + bool prmgrAdded = false; bool prdStarted = false; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virJSONValuePtr secobjProps = NULL; virJSONValuePtr encobjProps = NULL; + virJSONValuePtr prmgrProps = NULL; qemuDomainStorageSourcePrivatePtr srcPriv; qemuDomainSecretInfoPtr secinfo = NULL; qemuDomainSecretInfoPtr encinfo = NULL; + qemuDomainDiskPRDPtr prd = NULL;
if (qemuHotplugPrepareDiskAccess(driver, vm, disk, NULL, false) < 0) goto cleanup; @@ -437,6 +463,7 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (srcPriv) { secinfo = srcPriv->secinfo; encinfo = srcPriv->encinfo; + prd = srcPriv->prd;
As noted much earlier - the private data isn't needed here as the prd is in the disk storage definition.
}
if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) { @@ -447,6 +474,9 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (encinfo && qemuBuildSecretInfoProps(encinfo, &encobjProps) < 0) goto error;
+ if (qemuMaybeBuildPRManagerInfoProps(vm, prd, &prmgrProps) < 0) + goto error; +
This should just call qemuBuildPRManagerInfoProps directly since it already checks if !prd || !prd->alias John
if (disk->src->haveTLS && qemuDomainAddDiskSrcTLSObject(driver, vm, disk->src, disk->info.alias) < 0) @@ -484,6 +514,15 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, encobjAdded = true; }
+ if (prmgrProps) { + rv = qemuMonitorAddObject(priv->mon, "pr-manager-helper", prd->alias, + prmgrProps); + prmgrProps = NULL; /* qemuMonitorAddObject consumes */ + if (rv < 0) + goto exit_monitor; + prmgrAdded = true; + } + if (qemuMonitorAddDrive(priv->mon, drivestr) < 0) goto exit_monitor; driveAdded = true; @@ -521,6 +560,8 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, ignore_value(qemuMonitorDelObject(priv->mon, secinfo->s.aes.alias)); if (encobjAdded) ignore_value(qemuMonitorDelObject(priv->mon, encinfo->s.aes.alias)); + if (prmgrAdded) + ignore_value(qemuMonitorDelObject(priv->mon, prd->alias)); if (qemuDomainObjExitMonitor(driver, vm) < 0) ret = -2; virErrorRestore(&orig_err);

On 04/14/2018 05:14 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
When attaching a disk that requires pr-manager we might need to plug the pr-manager object too.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_hotplug.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+)
My opinion - combine w/ previous patch.
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 468153c79c..43bb910ed6 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -391,6 +391,29 @@ qemuDomainMaybeStartPRDaemon(virDomainObjPtr vm, }
+static int +qemuMaybeBuildPRManagerInfoProps(virDomainObjPtr vm, + qemuDomainDiskPRDPtr prd, + virJSONValuePtr *propsret) +{ + size_t i; + + *propsret = NULL; + + for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *domainDisk = vm->def->disks[i]; + + if (virStoragePRDefIsManaged(domainDisk->src->pr)) { + /* qemu-pr-helper should be already started because + * another disk in domain requires it. */ + return 0; + } + }
I don't understand the need for the above hunk - that was done in patch11 wasn't it? This is why I like things combined - going back and forth and changing logic as patches are developed means something may be missed.
Okay the comment might be misleading. It should be s/started/added/. Anwyay, when adding a disk that has PR configured we need to: 1) start pr-helper process (if we are the ones managing it), 2) construct JSON for pr-manager object, 3) add pr-mananager object on the monitor, 4) finally, add disk on the monitor. And we need to do it in this order, otherwise qemu might try to connect to not yet running process. Now, obviously, since there can be only one managed pr-helper process per domain, there can be only one pr-manager object. Therefore, when somebody is trying to hotplug a disk with PR enabled & managed, but there already is a disk like that: step 1) turns into no-op (see previous patch), and we also need steps 2) and 3) to turn into no-op because the object is already in qemu. Trying to add it again would result in error.
+ + return qemuBuildPRManagerInfoProps(prd, propsret); +} + + /** * qemuDomainAttachDiskGeneric: * @@ -411,13 +434,16 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, bool driveAdded = false; bool secobjAdded = false; bool encobjAdded = false; + bool prmgrAdded = false; bool prdStarted = false; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virJSONValuePtr secobjProps = NULL; virJSONValuePtr encobjProps = NULL; + virJSONValuePtr prmgrProps = NULL; qemuDomainStorageSourcePrivatePtr srcPriv; qemuDomainSecretInfoPtr secinfo = NULL; qemuDomainSecretInfoPtr encinfo = NULL; + qemuDomainDiskPRDPtr prd = NULL;
if (qemuHotplugPrepareDiskAccess(driver, vm, disk, NULL, false) < 0) goto cleanup; @@ -437,6 +463,7 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (srcPriv) { secinfo = srcPriv->secinfo; encinfo = srcPriv->encinfo; + prd = srcPriv->prd;
As noted much earlier - the private data isn't needed here as the prd is in the disk storage definition.
}
if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) { @@ -447,6 +474,9 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (encinfo && qemuBuildSecretInfoProps(encinfo, &encobjProps) < 0) goto error;
+ if (qemuMaybeBuildPRManagerInfoProps(vm, prd, &prmgrProps) < 0) + goto error; +
This should just call qemuBuildPRManagerInfoProps directly since it already checks if !prd || !prd->alias
No, because we might end up trying to add pr-manager object that is already there in qemu which would fail and so would whole hotplug opertaion. Michal

On 04/16/2018 10:57 AM, Michal Privoznik wrote:
On 04/14/2018 05:14 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
When attaching a disk that requires pr-manager we might need to plug the pr-manager object too.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_hotplug.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+)
My opinion - combine w/ previous patch.
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 468153c79c..43bb910ed6 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -391,6 +391,29 @@ qemuDomainMaybeStartPRDaemon(virDomainObjPtr vm, }
+static int +qemuMaybeBuildPRManagerInfoProps(virDomainObjPtr vm, + qemuDomainDiskPRDPtr prd, + virJSONValuePtr *propsret) +{ + size_t i; + + *propsret = NULL; + + for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *domainDisk = vm->def->disks[i]; + + if (virStoragePRDefIsManaged(domainDisk->src->pr)) { + /* qemu-pr-helper should be already started because + * another disk in domain requires it. */ + return 0; + } + }
I don't understand the need for the above hunk - that was done in patch11 wasn't it? This is why I like things combined - going back and forth and changing logic as patches are developed means something may be missed.
Okay the comment might be misleading. It should be s/started/added/.
Anwyay, when adding a disk that has PR configured we need to:
1) start pr-helper process (if we are the ones managing it), 2) construct JSON for pr-manager object, 3) add pr-mananager object on the monitor, 4) finally, add disk on the monitor.
And we need to do it in this order, otherwise qemu might try to connect to not yet running process. Now, obviously, since there can be only one managed pr-helper process per domain, there can be only one pr-manager object. Therefore, when somebody is trying to hotplug a disk with PR enabled & managed, but there already is a disk like that: step 1) turns into no-op (see previous patch), and we also need steps 2) and 3) to turn into no-op because the object is already in qemu. Trying to add it again would result in error.
qemuDomainMaybeStartPRDaemon which is a few lines above here and in the previous patch is what handled potentially starting the PR Daemon. Once I get here, then I'm running through the same list, doing the same check, and then deciding whether to create the object, but that would seem to be the task of the previous patch, but they're intertwined dependencies, so IMO, one patch. Since adding the ",file.pr-manager=%s" is part of qemuBuildDriveDevStr, it's not like anything done in this patch that's doing anything that couldn't be combined with the previous one.
+ + return qemuBuildPRManagerInfoProps(prd, propsret); +} + + /** * qemuDomainAttachDiskGeneric: * @@ -411,13 +434,16 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, bool driveAdded = false; bool secobjAdded = false; bool encobjAdded = false; + bool prmgrAdded = false; bool prdStarted = false; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virJSONValuePtr secobjProps = NULL; virJSONValuePtr encobjProps = NULL; + virJSONValuePtr prmgrProps = NULL; qemuDomainStorageSourcePrivatePtr srcPriv; qemuDomainSecretInfoPtr secinfo = NULL; qemuDomainSecretInfoPtr encinfo = NULL; + qemuDomainDiskPRDPtr prd = NULL;
if (qemuHotplugPrepareDiskAccess(driver, vm, disk, NULL, false) < 0) goto cleanup; @@ -437,6 +463,7 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (srcPriv) { secinfo = srcPriv->secinfo; encinfo = srcPriv->encinfo; + prd = srcPriv->prd;
As noted much earlier - the private data isn't needed here as the prd is in the disk storage definition.
}
if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) { @@ -447,6 +474,9 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (encinfo && qemuBuildSecretInfoProps(encinfo, &encobjProps) < 0) goto error;
+ if (qemuMaybeBuildPRManagerInfoProps(vm, prd, &prmgrProps) < 0) + goto error; +
This should just call qemuBuildPRManagerInfoProps directly since it already checks if !prd || !prd->alias
No, because we might end up trying to add pr-manager object that is already there in qemu which would fail and so would whole hotplug opertaion.
Of course the comment came from the same notion that running through ndisks again isn't really necessary and my continued thought that the 11 and 12 should be combined. John

[...]
* @@ -411,13 +434,16 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, bool driveAdded = false; bool secobjAdded = false; bool encobjAdded = false; + bool prmgrAdded = false; bool prdStarted = false; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virJSONValuePtr secobjProps = NULL; virJSONValuePtr encobjProps = NULL; + virJSONValuePtr prmgrProps = NULL; qemuDomainStorageSourcePrivatePtr srcPriv; qemuDomainSecretInfoPtr secinfo = NULL; qemuDomainSecretInfoPtr encinfo = NULL; + qemuDomainDiskPRDPtr prd = NULL;
if (qemuHotplugPrepareDiskAccess(driver, vm, disk, NULL, false) < 0) goto cleanup; @@ -437,6 +463,7 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (srcPriv) { secinfo = srcPriv->secinfo; encinfo = srcPriv->encinfo; + prd = srcPriv->prd; }
if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) { @@ -447,6 +474,9 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (encinfo && qemuBuildSecretInfoProps(encinfo, &encobjProps) < 0) goto error;
+ if (qemuMaybeBuildPRManagerInfoProps(vm, prd, &prmgrProps) < 0) + goto error; + if (disk->src->haveTLS && qemuDomainAddDiskSrcTLSObject(driver, vm, disk->src, disk->info.alias) < 0) @@ -484,6 +514,15 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, encobjAdded = true; }
+ if (prmgrProps) { + rv = qemuMonitorAddObject(priv->mon, "pr-manager-helper", prd->alias, + prmgrProps); + prmgrProps = NULL; /* qemuMonitorAddObject consumes */ + if (rv < 0) + goto exit_monitor; + prmgrAdded = true; + } +
Oh yeah - coverity let me know that @prmgrProps could be leaked if we don't get this far - need to add the virJSONValueFree cleanup
if (qemuMonitorAddDrive(priv->mon, drivestr) < 0) goto exit_monitor; driveAdded = true;
[...] John

On 04/14/2018 05:18 PM, John Ferlan wrote:
[...]
* @@ -411,13 +434,16 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, bool driveAdded = false; bool secobjAdded = false; bool encobjAdded = false; + bool prmgrAdded = false; bool prdStarted = false; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virJSONValuePtr secobjProps = NULL; virJSONValuePtr encobjProps = NULL; + virJSONValuePtr prmgrProps = NULL; qemuDomainStorageSourcePrivatePtr srcPriv; qemuDomainSecretInfoPtr secinfo = NULL; qemuDomainSecretInfoPtr encinfo = NULL; + qemuDomainDiskPRDPtr prd = NULL;
if (qemuHotplugPrepareDiskAccess(driver, vm, disk, NULL, false) < 0) goto cleanup; @@ -437,6 +463,7 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (srcPriv) { secinfo = srcPriv->secinfo; encinfo = srcPriv->encinfo; + prd = srcPriv->prd; }
if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) { @@ -447,6 +474,9 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, if (encinfo && qemuBuildSecretInfoProps(encinfo, &encobjProps) < 0) goto error;
+ if (qemuMaybeBuildPRManagerInfoProps(vm, prd, &prmgrProps) < 0) + goto error; + if (disk->src->haveTLS && qemuDomainAddDiskSrcTLSObject(driver, vm, disk->src, disk->info.alias) < 0) @@ -484,6 +514,15 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver, encobjAdded = true; }
+ if (prmgrProps) { + rv = qemuMonitorAddObject(priv->mon, "pr-manager-helper", prd->alias, + prmgrProps); + prmgrProps = NULL; /* qemuMonitorAddObject consumes */ + if (rv < 0) + goto exit_monitor; + prmgrAdded = true; + } +
Oh yeah - coverity let me know that @prmgrProps could be leaked if we don't get this far - need to add the virJSONValueFree cleanup
Ah, sure. Nice catch. Michal

If we are the last one to use pr-manager object we need to remove it and also kill the qemu-pr-helper process. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_hotplug.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 43bb910ed6..98e1bf7082 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -3894,6 +3894,34 @@ static bool qemuIsMultiFunctionDevice(virDomainDefPtr def, } +static qemuDomainDiskPRDPtr +qemuDomainDiskNeedRemovePR(virDomainObjPtr vm, + virDomainDiskDefPtr disk) +{ + qemuDomainStorageSourcePrivatePtr srcPriv; + size_t i; + + if (!virStoragePRDefIsManaged(disk->src->pr)) + return NULL; + + for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *domainDisk = vm->def->disks[i]; + + if (domainDisk == disk) + continue; + + if (virStoragePRDefIsManaged(domainDisk->src->pr)) + break; + } + + if (i != vm->def->ndisks) + return NULL; + + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + return srcPriv->prd; +} + + static int qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -3907,6 +3935,7 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, char *drivestr; char *objAlias = NULL; char *encAlias = NULL; + qemuDomainDiskPRDPtr prd = NULL; VIR_DEBUG("Removing disk %s from domain %p %s", disk->info.alias, vm, vm->def->name); @@ -3944,6 +3973,8 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, } } + prd = qemuDomainDiskNeedRemovePR(vm, disk); + qemuDomainObjEnterMonitor(driver, vm); qemuMonitorDriveDel(priv->mon, drivestr); @@ -3959,6 +3990,10 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, ignore_value(qemuMonitorDelObject(priv->mon, encAlias)); VIR_FREE(encAlias); + /* If it fails, then so be it - it was a best shot */ + if (prd) + ignore_value(qemuMonitorDelObject(priv->mon, prd->alias)); + if (disk->src->haveTLS) ignore_value(qemuMonitorDelObject(priv->mon, disk->src->tlsAlias)); @@ -3977,6 +4012,9 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, } } + if (prd) + qemuProcessKillPRDaemon(vm); + qemuDomainReleaseDeviceAddress(vm, &disk->info, src); if (qemuSecurityRestoreDiskLabel(driver, vm, disk) < 0) -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
If we are the last one to use pr-manager object we need to remove it and also kill the qemu-pr-helper process.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_hotplug.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
Again, my opinion this is combined too - patch is larger, but everything is covered at one time.
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 43bb910ed6..98e1bf7082 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -3894,6 +3894,34 @@ static bool qemuIsMultiFunctionDevice(virDomainDefPtr def, }
+static qemuDomainDiskPRDPtr +qemuDomainDiskNeedRemovePR(virDomainObjPtr vm, + virDomainDiskDefPtr disk) +{ + qemuDomainStorageSourcePrivatePtr srcPriv; + size_t i; + + if (!virStoragePRDefIsManaged(disk->src->pr)) + return NULL; + + for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *domainDisk = vm->def->disks[i]; + + if (domainDisk == disk) + continue; + + if (virStoragePRDefIsManaged(domainDisk->src->pr)) + break; + } + + if (i != vm->def->ndisks) + return NULL; + + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + return srcPriv->prd; +} + +
Trying to combine two separate things into one? Thing 1 -> Remove the object from the domain Thing 2 -> Kill the PRD helper *iff* this is the last one using it
static int qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -3907,6 +3935,7 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, char *drivestr; char *objAlias = NULL; char *encAlias = NULL; + qemuDomainDiskPRDPtr prd = NULL;
VIR_DEBUG("Removing disk %s from domain %p %s", disk->info.alias, vm, vm->def->name); @@ -3944,6 +3973,8 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, } }
+ prd = qemuDomainDiskNeedRemovePR(vm, disk); + qemuDomainObjEnterMonitor(driver, vm);
qemuMonitorDriveDel(priv->mon, drivestr); @@ -3959,6 +3990,10 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, ignore_value(qemuMonitorDelObject(priv->mon, encAlias)); VIR_FREE(encAlias);
+ /* If it fails, then so be it - it was a best shot */ + if (prd) + ignore_value(qemuMonitorDelObject(priv->mon, prd->alias)); +
OK - remove the object if we have one...
if (disk->src->haveTLS) ignore_value(qemuMonitorDelObject(priv->mon, disk->src->tlsAlias));
@@ -3977,6 +4012,9 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, } }
+ if (prd) + qemuProcessKillPRDaemon(vm); +
But we only want to kill if this if there are no other disk's needing the pr-helper, right? So we need to have a routine that would be run after the disk is really removed. The two phase removal process always gets me ;-) - that is Detach vs. Remove and which one is last *after* the disk is removed from the list of disks *and* when we know the object has been removed from the domain, then we can stop the PR process iff this is the last one using it. John
qemuDomainReleaseDeviceAddress(vm, &disk->info, src);
if (qemuSecurityRestoreDiskLabel(driver, vm, disk) < 0)

On 04/14/2018 05:14 PM, John Ferlan wrote:
On 04/10/2018 10:58 AM, Michal Privoznik wrote:
If we are the last one to use pr-manager object we need to remove it and also kill the qemu-pr-helper process.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_hotplug.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
Again, my opinion this is combined too - patch is larger, but everything is covered at one time.
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 43bb910ed6..98e1bf7082 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -3894,6 +3894,34 @@ static bool qemuIsMultiFunctionDevice(virDomainDefPtr def, }
+static qemuDomainDiskPRDPtr +qemuDomainDiskNeedRemovePR(virDomainObjPtr vm, + virDomainDiskDefPtr disk) +{ + qemuDomainStorageSourcePrivatePtr srcPriv; + size_t i; + + if (!virStoragePRDefIsManaged(disk->src->pr)) + return NULL; + + for (i = 0; i < vm->def->ndisks; i++) { + const virDomainDiskDef *domainDisk = vm->def->disks[i]; + + if (domainDisk == disk) + continue; + + if (virStoragePRDefIsManaged(domainDisk->src->pr)) + break; + } + + if (i != vm->def->ndisks) + return NULL; + + srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + return srcPriv->prd; +} + +
Trying to combine two separate things into one?
Thing 1 -> Remove the object from the domain Thing 2 -> Kill the PRD helper *iff* this is the last one using it
static int qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -3907,6 +3935,7 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, char *drivestr; char *objAlias = NULL; char *encAlias = NULL; + qemuDomainDiskPRDPtr prd = NULL;
VIR_DEBUG("Removing disk %s from domain %p %s", disk->info.alias, vm, vm->def->name); @@ -3944,6 +3973,8 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, } }
+ prd = qemuDomainDiskNeedRemovePR(vm, disk); + qemuDomainObjEnterMonitor(driver, vm);
qemuMonitorDriveDel(priv->mon, drivestr); @@ -3959,6 +3990,10 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, ignore_value(qemuMonitorDelObject(priv->mon, encAlias)); VIR_FREE(encAlias);
+ /* If it fails, then so be it - it was a best shot */ + if (prd) + ignore_value(qemuMonitorDelObject(priv->mon, prd->alias)); +
OK - remove the object if we have one...
if (disk->src->haveTLS) ignore_value(qemuMonitorDelObject(priv->mon, disk->src->tlsAlias));
@@ -3977,6 +4012,9 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, } }
+ if (prd) + qemuProcessKillPRDaemon(vm); +
But we only want to kill if this if there are no other disk's needing the pr-helper, right? So we need to have a routine that would be run after the disk is really removed. The two phase removal process always gets me ;-) - that is Detach vs. Remove and which one is last *after* the disk is removed from the list of disks *and* when we know the object has been removed from the domain, then we can stop the PR process iff this is the last one using it.
Detach is called first and then, when guest OS stopped using the device (disk in this case) and so did qemu Remove is called. And we have to do the steps I described in 12/14 but in reverse order: 1) remove the disk (done already, no special handling from this feature POV), 2) remove pr-manager object (if no other disk still uses it), 3) kill the pr-helper process. The check from step 2) is done in qemuDomainDiskNeedRemovePR() and on success (=kill & remove) a non-NULL pointer is returned. Michal

Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 1 + tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml | 1 + 6 files changed, 6 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index efd468692a..4a3afa7668 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -1105,6 +1105,7 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { { "virtio-mouse-ccw", QEMU_CAPS_DEVICE_VIRTIO_MOUSE_CCW }, { "virtio-tablet-ccw", QEMU_CAPS_DEVICE_VIRTIO_TABLET_CCW }, { "pcie-pci-bridge", QEMU_CAPS_DEVICE_PCIE_PCI_BRIDGE }, + { "pr-manager-helper", QEMU_CAPS_PR_MANAGER_HELPER }, }; static struct virQEMUCapsStringFlags virQEMUCapsObjectPropsVirtioBalloon[] = { diff --git a/tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml b/tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml index cbd645ae93..4e6e77a57f 100644 --- a/tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml +++ b/tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml @@ -151,6 +151,7 @@ <flag name='virtio-mouse-ccw'/> <flag name='virtio-tablet-ccw'/> <flag name='qcow2-luks'/> + <flag name='pr-manager-helper'/> <version>2011000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>342058</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml b/tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml index 66629ff5bc..d31ad0d13f 100644 --- a/tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml @@ -189,6 +189,7 @@ <flag name='dump-completed'/> <flag name='qcow2-luks'/> <flag name='pcie-pci-bridge'/> + <flag name='pr-manager-helper'/> <version>2011090</version> <kvmVersion>0</kvmVersion> <microcodeVersion>342346</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml b/tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml index 1122d6408b..db455227f7 100644 --- a/tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml +++ b/tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml @@ -186,6 +186,7 @@ <flag name='machine.pseries.max-cpu-compat'/> <flag name='dump-completed'/> <flag name='qcow2-luks'/> + <flag name='pr-manager-helper'/> <version>2011090</version> <kvmVersion>0</kvmVersion> <microcodeVersion>419215</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml b/tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml index 191b1e0e37..19a7b97967 100644 --- a/tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml +++ b/tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml @@ -151,6 +151,7 @@ <flag name='virtio-mouse-ccw'/> <flag name='virtio-tablet-ccw'/> <flag name='qcow2-luks'/> + <flag name='pr-manager-helper'/> <version>2011090</version> <kvmVersion>0</kvmVersion> <microcodeVersion>0</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml index 4ed2e1ea96..0105c99687 100644 --- a/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml @@ -227,6 +227,7 @@ <flag name='dump-completed'/> <flag name='qcow2-luks'/> <flag name='pcie-pci-bridge'/> + <flag name='pr-manager-helper'/> <version>2011090</version> <kvmVersion>0</kvmVersion> <microcodeVersion>390060</microcodeVersion> -- 2.16.1

On 04/10/2018 10:58 AM, Michal Privoznik wrote:
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 1 + tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml | 1 + 6 files changed, 6 insertions(+)
Reviewed-by: John Ferlan <jferlan@redhat.com> John
participants (2)
-
John Ferlan
-
Michal Privoznik