mirror of https://gitee.com/openkylin/libvirt.git
nodedev: Implement virNodeDeviceUpdate
Implement the API functions in the node device driver by using mdevctl modify with the options defined and live. Instead of increasing the minimum mdevctl version to 1.3.0 in the spec file to ensure support exists in mdevctl the support is dynamically checked before using mdevctl. Signed-off-by: Boris Fiuczynski <fiuczy@linux.ibm.com> Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Jonathon Jongsma <jjongsma@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
parent
69f9e7dbc2
commit
582f27ff15
7
NEWS.rst
7
NEWS.rst
|
@ -17,6 +17,13 @@ v10.1.0 (unreleased)
|
|||
|
||||
* **New features**
|
||||
|
||||
* nodedev: Support updating mdevs
|
||||
|
||||
The node device driver has been extended to allow updating mediated node
|
||||
devices. Options are available to target the update against the persistent,
|
||||
active or both configurations of a mediated device.
|
||||
**Note:** The support is only available with at least mdevctl v1.3.0 installed.
|
||||
|
||||
* qemu: Add support for /dev/userfaultfd
|
||||
|
||||
On hosts with new enough kernel which supports /dev/userfaultfd libvirt will
|
||||
|
|
|
@ -620,6 +620,7 @@ Requires: libvirt-libs = %{version}-%{release}
|
|||
# needed for device enumeration
|
||||
Requires: systemd >= 185
|
||||
# For managing persistent mediated devices
|
||||
# Note: for nodedev-update support at least mdevctl v1.3.0 is required
|
||||
Requires: mdevctl
|
||||
# for modprobe of pci devices
|
||||
Requires: module-init-tools
|
||||
|
|
|
@ -54,7 +54,7 @@ virNodeDeviceDriverState *driver;
|
|||
|
||||
VIR_ENUM_IMPL(virMdevctlCommand,
|
||||
MDEVCTL_CMD_LAST,
|
||||
"start", "stop", "define", "undefine", "create"
|
||||
"start", "stop", "define", "undefine", "create", "modify"
|
||||
);
|
||||
|
||||
|
||||
|
@ -754,6 +754,7 @@ nodeDeviceGetMdevctlCommand(virNodeDeviceDef *def,
|
|||
case MDEVCTL_CMD_START:
|
||||
case MDEVCTL_CMD_DEFINE:
|
||||
case MDEVCTL_CMD_UNDEFINE:
|
||||
case MDEVCTL_CMD_MODIFY:
|
||||
cmd = virCommandNewArgList(MDEVCTL, subcommand, NULL);
|
||||
break;
|
||||
case MDEVCTL_CMD_LAST:
|
||||
|
@ -767,6 +768,7 @@ nodeDeviceGetMdevctlCommand(virNodeDeviceDef *def,
|
|||
switch (cmd_type) {
|
||||
case MDEVCTL_CMD_CREATE:
|
||||
case MDEVCTL_CMD_DEFINE:
|
||||
case MDEVCTL_CMD_MODIFY:
|
||||
if (!def->caps->data.mdev.parent_addr) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("unable to find parent device '%1$s'"), def->parent);
|
||||
|
@ -783,7 +785,8 @@ nodeDeviceGetMdevctlCommand(virNodeDeviceDef *def,
|
|||
virCommandAddArgPair(cmd, "--jsonfile", "/dev/stdin");
|
||||
|
||||
virCommandSetInputBuffer(cmd, inbuf);
|
||||
virCommandSetOutputBuffer(cmd, outbuf);
|
||||
if (outbuf)
|
||||
virCommandSetOutputBuffer(cmd, outbuf);
|
||||
break;
|
||||
|
||||
case MDEVCTL_CMD_UNDEFINE:
|
||||
|
@ -868,6 +871,102 @@ virMdevctlDefine(virNodeDeviceDef *def, char **uuid)
|
|||
}
|
||||
|
||||
|
||||
/* gets a virCommand object that executes a mdevctl command to modify a
|
||||
* a device to an updated version
|
||||
*/
|
||||
virCommand*
|
||||
nodeDeviceGetMdevctlModifyCommand(virNodeDeviceDef *def,
|
||||
bool defined,
|
||||
bool live,
|
||||
char **errmsg)
|
||||
{
|
||||
virCommand *cmd = nodeDeviceGetMdevctlCommand(def,
|
||||
MDEVCTL_CMD_MODIFY,
|
||||
NULL, errmsg);
|
||||
|
||||
if (!cmd)
|
||||
return NULL;
|
||||
|
||||
if (defined)
|
||||
virCommandAddArg(cmd, "--defined");
|
||||
|
||||
if (live)
|
||||
virCommandAddArg(cmd, "--live");
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
|
||||
/* checks if mdevctl supports on the command modify the options live, defined
|
||||
* and jsonfile
|
||||
*/
|
||||
static int
|
||||
nodeDeviceGetMdevctlModifySupportCheck(void)
|
||||
{
|
||||
int status;
|
||||
g_autoptr(virCommand) cmd = NULL;
|
||||
const char *subcommand = virMdevctlCommandTypeToString(MDEVCTL_CMD_MODIFY);
|
||||
|
||||
cmd = virCommandNewArgList(MDEVCTL,
|
||||
subcommand,
|
||||
"--defined",
|
||||
"--live",
|
||||
"--jsonfile",
|
||||
"b",
|
||||
"--help",
|
||||
NULL);
|
||||
|
||||
if (!cmd)
|
||||
return -1;
|
||||
|
||||
if (virCommandRun(cmd, &status) < 0)
|
||||
return -1;
|
||||
|
||||
if (status != 0) {
|
||||
/* update is unsupported */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virMdevctlModify(virNodeDeviceDef *def,
|
||||
bool defined,
|
||||
bool live)
|
||||
{
|
||||
int status;
|
||||
g_autofree char *errmsg = NULL;
|
||||
g_autoptr(virCommand) cmd = nodeDeviceGetMdevctlModifyCommand(def,
|
||||
defined,
|
||||
live,
|
||||
&errmsg);
|
||||
|
||||
if (!cmd)
|
||||
return -1;
|
||||
|
||||
if (nodeDeviceGetMdevctlModifySupportCheck() < 0) {
|
||||
VIR_WARN("Installed mdevctl version does not support modify with options jsonfile, defined and live");
|
||||
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
||||
_("Unable to modify mediated device: modify unsupported"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (virCommandRun(cmd, &status) < 0)
|
||||
return -1;
|
||||
|
||||
if (status != 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("Unable to modify mediated device: %1$s"),
|
||||
MDEVCTL_ERROR(errmsg));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static virNodeDevicePtr
|
||||
nodeDeviceCreateXMLMdev(virConnectPtr conn,
|
||||
virNodeDeviceDef *def)
|
||||
|
@ -2107,3 +2206,128 @@ nodeDeviceIsActive(virNodeDevice *device)
|
|||
virNodeDeviceObjEndAPI(&obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
nodeDeviceDefValidateUpdate(virNodeDeviceDef *def,
|
||||
virNodeDeviceDef *new_def,
|
||||
bool live)
|
||||
{
|
||||
virNodeDevCapsDef *caps = NULL;
|
||||
virNodeDevCapMdev *cap_mdev = NULL;
|
||||
virNodeDevCapMdev *cap_new_mdev = NULL;
|
||||
|
||||
for (caps = def->caps; caps != NULL; caps = caps->next) {
|
||||
if (caps->data.type == VIR_NODE_DEV_CAP_MDEV) {
|
||||
cap_mdev = &caps->data.mdev;
|
||||
}
|
||||
}
|
||||
if (!cap_mdev)
|
||||
return -1;
|
||||
|
||||
for (caps = new_def->caps; caps != NULL; caps = caps->next) {
|
||||
if (caps->data.type == VIR_NODE_DEV_CAP_MDEV) {
|
||||
cap_new_mdev = &caps->data.mdev;
|
||||
}
|
||||
}
|
||||
if (!cap_new_mdev)
|
||||
return -1;
|
||||
|
||||
if (STRNEQ_NULLABLE(cap_mdev->uuid, cap_new_mdev->uuid)) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("Cannot update device '%1$s, uuid mismatch (current uuid '%2$s')"),
|
||||
def->name,
|
||||
cap_mdev->uuid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* A live update cannot change the mdev type. Since the new config is
|
||||
* stored in defined_config, compare that to the mdev type of the current
|
||||
* live config to make sure they match */
|
||||
if (live &&
|
||||
STRNEQ_NULLABLE(cap_mdev->active_config.type, cap_new_mdev->defined_config.type)) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("Cannot update device '%1$s', type mismatch (current type '%2$s')"),
|
||||
def->name,
|
||||
cap_mdev->active_config.type);
|
||||
return -1;
|
||||
}
|
||||
if (STRNEQ_NULLABLE(cap_mdev->parent_addr, cap_new_mdev->parent_addr)) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("Cannot update device '%1$s', parent address mismatch (current parent address '%2$s')"),
|
||||
def->name,
|
||||
cap_mdev->parent_addr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
nodeDeviceUpdate(virNodeDevice *device,
|
||||
const char *xmlDesc,
|
||||
unsigned int flags)
|
||||
{
|
||||
int ret = -1;
|
||||
virNodeDeviceObj *obj = NULL;
|
||||
virNodeDeviceDef *def;
|
||||
g_autoptr(virNodeDeviceDef) new_def = NULL;
|
||||
const char *virt_type = NULL;
|
||||
bool updated = false;
|
||||
|
||||
virCheckFlags(VIR_NODE_DEVICE_UPDATE_AFFECT_LIVE |
|
||||
VIR_NODE_DEVICE_UPDATE_AFFECT_CONFIG, -1);
|
||||
|
||||
if (nodeDeviceInitWait() < 0)
|
||||
return -1;
|
||||
|
||||
if (!(obj = nodeDeviceObjFindByName(device->name)))
|
||||
return -1;
|
||||
def = virNodeDeviceObjGetDef(obj);
|
||||
|
||||
virt_type = virConnectGetType(device->conn);
|
||||
|
||||
if (virNodeDeviceUpdateEnsureACL(device->conn, def, flags) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!(new_def = virNodeDeviceDefParse(xmlDesc, NULL, EXISTING_DEVICE, virt_type,
|
||||
&driver->parserCallbacks, NULL, true)))
|
||||
goto cleanup;
|
||||
|
||||
if (nodeDeviceHasCapability(def, VIR_NODE_DEV_CAP_MDEV) &&
|
||||
nodeDeviceHasCapability(new_def, VIR_NODE_DEV_CAP_MDEV)) {
|
||||
/* Checks flags are valid for the state and sets flags for
|
||||
* current if flags not set. */
|
||||
if (virNodeDeviceObjUpdateModificationImpact(obj, &flags) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* Compare def and new_def for compatibility e.g. parent, type
|
||||
* and uuid. */
|
||||
if (nodeDeviceDefValidateUpdate(def, new_def,
|
||||
(flags & VIR_NODE_DEVICE_UPDATE_AFFECT_LIVE)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* Update now. */
|
||||
VIR_DEBUG("Update node device '%s' with mdevctl", def->name);
|
||||
if (virMdevctlModify(new_def,
|
||||
(flags & VIR_NODE_DEVICE_UPDATE_AFFECT_CONFIG),
|
||||
(flags & VIR_NODE_DEVICE_UPDATE_AFFECT_LIVE)) < 0) {
|
||||
goto cleanup;
|
||||
};
|
||||
/* Detect updates and also trigger events. */
|
||||
updated = true;
|
||||
} else {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||
_("Unsupported device type"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
virNodeDeviceObjEndAPI(&obj);
|
||||
if (updated)
|
||||
nodeDeviceUpdateMediatedDevices();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ typedef enum {
|
|||
* separation makes our code more readable in terms of knowing when we're
|
||||
* starting a defined device and when we're creating a transient one */
|
||||
MDEVCTL_CMD_CREATE,
|
||||
MDEVCTL_CMD_MODIFY,
|
||||
|
||||
MDEVCTL_CMD_LAST,
|
||||
} virMdevctlCommand;
|
||||
|
@ -186,3 +187,13 @@ virCommand*
|
|||
nodeDeviceGetMdevctlSetAutostartCommand(virNodeDeviceDef *def,
|
||||
bool autostart,
|
||||
char **errmsg);
|
||||
|
||||
virCommand*
|
||||
nodeDeviceGetMdevctlModifyCommand(virNodeDeviceDef *def,
|
||||
bool defined,
|
||||
bool live,
|
||||
char **errmsg);
|
||||
int
|
||||
nodeDeviceUpdate(virNodeDevice *dev,
|
||||
const char *xmlDesc,
|
||||
unsigned int flags);
|
||||
|
|
|
@ -2403,6 +2403,7 @@ static virNodeDeviceDriver udevNodeDeviceDriver = {
|
|||
.nodeDeviceGetAutostart = nodeDeviceGetAutostart, /* 7.8.0 */
|
||||
.nodeDeviceIsPersistent = nodeDeviceIsPersistent, /* 7.8.0 */
|
||||
.nodeDeviceIsActive = nodeDeviceIsActive, /* 7.8.0 */
|
||||
.nodeDeviceUpdate = nodeDeviceUpdate, /* 10.1.0 */
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<device>
|
||||
<name>mdev_c60cc60c_c60c_c60c_c60c_c60cc60cc60c_0_0_0052</name>
|
||||
<path>/sys/devices/css0/0.0.0052/c60cc60c-c60c-c60c-c60c-c60cc60cc60c</path>
|
||||
<parent>css_0_0_0052</parent>
|
||||
<driver>
|
||||
<name>vfio_ccw_mdev</name>
|
||||
</driver>
|
||||
<capability type='mdev'>
|
||||
<type id='vfio_ccw-io'/>
|
||||
<uuid>c60cc60c-c60c-c60c-c60c-c60cc60cc60c</uuid>
|
||||
<parent_addr>0.0.0052</parent_addr>
|
||||
<attr name='add_attr_1' value='val1'/>
|
||||
<attr name='add_attr_2' value='val2'/>
|
||||
<iommuGroup number='4'/>
|
||||
</capability>
|
||||
</device>
|
|
@ -0,0 +1,25 @@
|
|||
mdevctl \
|
||||
modify \
|
||||
--parent=0.0.0052 \
|
||||
--jsonfile=/dev/stdin \
|
||||
--uuid=c60cc60c-c60c-c60c-c60c-c60cc60cc60c \
|
||||
--defined
|
||||
mdevctl \
|
||||
modify \
|
||||
--parent=0.0.0052 \
|
||||
--jsonfile=/dev/stdin \
|
||||
--uuid=c60cc60c-c60c-c60c-c60c-c60cc60cc60c \
|
||||
--live
|
||||
mdevctl \
|
||||
modify \
|
||||
--parent=0.0.0052 \
|
||||
--jsonfile=/dev/stdin \
|
||||
--uuid=c60cc60c-c60c-c60c-c60c-c60cc60cc60c \
|
||||
--live
|
||||
mdevctl \
|
||||
modify \
|
||||
--parent=0.0.0052 \
|
||||
--jsonfile=/dev/stdin \
|
||||
--uuid=c60cc60c-c60c-c60c-c60c-c60cc60cc60c \
|
||||
--defined \
|
||||
--live
|
|
@ -0,0 +1,4 @@
|
|||
{"mdev_type":"vfio_ccw-io","start":"manual"}
|
||||
{"mdev_type":"vfio_ccw-io","start":"manual","attrs":[{"add_attr_1":"val1"},{"add_attr_2":"val2"}]}
|
||||
{"mdev_type":"vfio_ccw-io","start":"manual"}
|
||||
{"mdev_type":"vfio_ccw-io","start":"manual","attrs":[{"add_attr_1":"val1"},{"add_attr_2":"val2"}]}
|
|
@ -33,7 +33,10 @@ testCommandDryRunCallback(const char *const*args G_GNUC_UNUSED,
|
|||
{
|
||||
char **stdinbuf = opaque;
|
||||
|
||||
*stdinbuf = g_strdup(input);
|
||||
if (*stdinbuf)
|
||||
*stdinbuf = g_strconcat(*stdinbuf, "\n", input, NULL);
|
||||
else
|
||||
*stdinbuf = g_strdup(input);
|
||||
}
|
||||
|
||||
typedef virCommand * (*MdevctlCmdFunc)(virNodeDeviceDef *, char **, char **);
|
||||
|
@ -63,6 +66,7 @@ testMdevctlCmd(virMdevctlCommand cmd_type,
|
|||
case MDEVCTL_CMD_START:
|
||||
case MDEVCTL_CMD_STOP:
|
||||
case MDEVCTL_CMD_UNDEFINE:
|
||||
case MDEVCTL_CMD_MODIFY:
|
||||
create = EXISTING_DEVICE;
|
||||
break;
|
||||
case MDEVCTL_CMD_LAST:
|
||||
|
@ -173,6 +177,85 @@ testMdevctlAutostart(const void *data G_GNUC_UNUSED)
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
testMdevctlModify(const void *data G_GNUC_UNUSED)
|
||||
{
|
||||
g_autoptr(virNodeDeviceDef) def = NULL;
|
||||
g_autoptr(virNodeDeviceDef) def_update = NULL;
|
||||
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||
const char *actualCmdline = NULL;
|
||||
int ret = -1;
|
||||
g_autoptr(virCommand) definedcmd = NULL;
|
||||
g_autoptr(virCommand) livecmd = NULL;
|
||||
g_autoptr(virCommand) bothcmd = NULL;
|
||||
g_autofree char *errmsg = NULL;
|
||||
g_autofree char *stdinbuf = NULL;
|
||||
g_autofree char *mdevxml =
|
||||
g_strdup_printf("%s/nodedevschemadata/mdev_c60cc60c_c60c_c60c_c60c_c60cc60cc60c.xml",
|
||||
abs_srcdir);
|
||||
g_autofree char *mdevxml_update =
|
||||
g_strdup_printf("%s/nodedevmdevctldata/mdev_c60cc60c_c60c_c60c_c60c_c60cc60cc60c_update.xml",
|
||||
abs_srcdir);
|
||||
/* just concatenate both calls into the same output files */
|
||||
g_autofree char *cmdlinefile =
|
||||
g_strdup_printf("%s/nodedevmdevctldata/mdevctl-modify.argv",
|
||||
abs_srcdir);
|
||||
g_autofree char *jsonfile =
|
||||
g_strdup_printf("%s/nodedevmdevctldata/mdevctl-modify.json",
|
||||
abs_srcdir);
|
||||
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
|
||||
|
||||
if (!(def = virNodeDeviceDefParse(NULL, mdevxml, CREATE_DEVICE, VIRT_TYPE,
|
||||
&parser_callbacks, NULL, false)))
|
||||
return -1;
|
||||
|
||||
virCommandSetDryRun(dryRunToken, &buf, true, true, testCommandDryRunCallback, &stdinbuf);
|
||||
|
||||
if (!(definedcmd = nodeDeviceGetMdevctlModifyCommand(def, true, false, &errmsg)))
|
||||
goto cleanup;
|
||||
|
||||
if (virCommandRun(definedcmd, NULL) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!(def_update = virNodeDeviceDefParse(NULL, mdevxml_update, EXISTING_DEVICE, VIRT_TYPE,
|
||||
&parser_callbacks, NULL, false)))
|
||||
goto cleanup;
|
||||
|
||||
if (!(livecmd = nodeDeviceGetMdevctlModifyCommand(def_update, false, true, &errmsg)))
|
||||
goto cleanup;
|
||||
|
||||
if (virCommandRun(livecmd, NULL) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!(livecmd = nodeDeviceGetMdevctlModifyCommand(def, false, true, &errmsg)))
|
||||
goto cleanup;
|
||||
|
||||
if (virCommandRun(livecmd, NULL) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!(bothcmd = nodeDeviceGetMdevctlModifyCommand(def_update, true, true, &errmsg)))
|
||||
goto cleanup;
|
||||
|
||||
if (virCommandRun(bothcmd, NULL) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!(actualCmdline = virBufferCurrentContent(&buf)))
|
||||
goto cleanup;
|
||||
|
||||
if (virTestCompareToFileFull(actualCmdline, cmdlinefile, false) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (virTestCompareToFile(stdinbuf, jsonfile) < 0)
|
||||
goto cleanup;
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
virBufferFreeAndReset(&buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
testMdevctlListDefined(const void *data G_GNUC_UNUSED)
|
||||
{
|
||||
|
@ -457,6 +540,9 @@ mymain(void)
|
|||
#define DO_TEST_AUTOSTART() \
|
||||
DO_TEST_FULL("autostart mdevs", testMdevctlAutostart, NULL)
|
||||
|
||||
#define DO_TEST_MODIFY() \
|
||||
DO_TEST_FULL("modify mdevs", testMdevctlModify, NULL)
|
||||
|
||||
#define DO_TEST_PARSE_JSON(filename) \
|
||||
DO_TEST_FULL("parse mdevctl json " filename, testMdevctlParse, filename)
|
||||
|
||||
|
@ -485,6 +571,8 @@ mymain(void)
|
|||
|
||||
DO_TEST_AUTOSTART();
|
||||
|
||||
DO_TEST_MODIFY();
|
||||
|
||||
done:
|
||||
nodedevTestDriverFree(driver);
|
||||
|
||||
|
|
Loading…
Reference in New Issue