Since qemu has supported memory-backend-ram and memory-backend-file object,
so we should add a new 'memdev' device type in <devices> XML to introduce
the
memory element, Its definition like the following:
<memdev type='ram' merge='yes' dump='yes'
prealloc='yes'>
<name>ram0</name>
<capacity unit='MiB'>1000</capacity>
<source host-nodes='0-1' policy='bind' />
</memdev>
then we enable to support -numa memdev=ram0 command line for binding guest
numa nodes to host numa nodes.
Signed-off-by: Chen Fan <chen.fan.fnst(a)cn.fujitsu.com>
---
src/conf/domain_conf.c | 203 ++++++++++++++++++++++++++++++++++++++++++-
src/conf/domain_conf.h | 42 +++++++++
src/libvirt_private.syms | 4 +
src/qemu/qemu_capabilities.c | 4 +
src/qemu/qemu_capabilities.h | 2 +
src/qemu/qemu_command.c | 74 ++++++++++++++++
src/qemu/qemu_command.h | 4 +
src/qemu/qemu_hotplug.c | 1 +
8 files changed, 333 insertions(+), 1 deletion(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index be81dbe..c55bf47 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -204,7 +204,8 @@ VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST,
"chr",
"memballoon",
"nvram",
- "rng")
+ "rng",
+ "memdev")
VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST,
"none",
@@ -454,6 +455,16 @@ VIR_ENUM_IMPL(virDomainMemballoonModel,
VIR_DOMAIN_MEMBALLOON_MODEL_LAST,
"xen",
"none")
+VIR_ENUM_IMPL(virDomainMemDev, VIR_DOMAIN_MEMDEV_LAST,
+ "ram",
+ "file")
+
+VIR_ENUM_IMPL(virDomainHostNodePolicy, VIR_DOMAIN_HOST_NODE_POLICY_LAST,
+ "default",
+ "preferred",
+ "bind",
+ "interleave")
+
VIR_ENUM_IMPL(virDomainSmbiosMode, VIR_DOMAIN_SMBIOS_LAST,
"none",
"emulate",
@@ -770,6 +781,10 @@ static void virDomainObjDispose(void *obj);
static void virDomainObjListDispose(void *obj);
static void virDomainXMLOptionClassDispose(void *obj);
+static int
+virDomainParseMemory(const char *xpath, xmlXPathContextPtr ctxt,
+ unsigned long long *mem, bool required);
+
static int virDomainObjOnceInit(void)
{
if (!(virDomainObjClass = virClassNew(virClassForObjectLockable(),
@@ -1661,6 +1676,18 @@ void virDomainMemballoonDefFree(virDomainMemballoonDefPtr def)
VIR_FREE(def);
}
+void virDomainMemDevDefFree(virDomainMemDevDefPtr def)
+{
+ if (!def)
+ return;
+
+ VIR_FREE(def->name);
+ VIR_FREE(def->mempath);
+ VIR_FREE(def->hostnodes);
+ VIR_FREE(def);
+}
+
+
void virDomainNVRAMDefFree(virDomainNVRAMDefPtr def)
{
if (!def)
@@ -1864,6 +1891,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
case VIR_DOMAIN_DEVICE_NVRAM:
virDomainNVRAMDefFree(def->data.nvram);
break;
+ case VIR_DOMAIN_DEVICE_MEMDEV:
+ virDomainMemDevDefFree(def->data.memdev);
+ break;
case VIR_DOMAIN_DEVICE_LAST:
case VIR_DOMAIN_DEVICE_NONE:
break;
@@ -2543,6 +2573,7 @@ virDomainDeviceGetInfo(virDomainDeviceDefPtr device)
/* The following devices do not contain virDomainDeviceInfo */
case VIR_DOMAIN_DEVICE_LEASE:
case VIR_DOMAIN_DEVICE_GRAPHICS:
+ case VIR_DOMAIN_DEVICE_MEMDEV:
case VIR_DOMAIN_DEVICE_LAST:
case VIR_DOMAIN_DEVICE_NONE:
break;
@@ -2780,6 +2811,7 @@ virDomainDeviceInfoIterateInternal(virDomainDefPtr def,
case VIR_DOMAIN_DEVICE_NVRAM:
case VIR_DOMAIN_DEVICE_LAST:
case VIR_DOMAIN_DEVICE_RNG:
+ case VIR_DOMAIN_DEVICE_MEMDEV:
break;
}
@@ -9192,6 +9224,104 @@ virDomainMemballoonDefParseXML(xmlNodePtr node,
goto cleanup;
}
+static virDomainMemDevDefPtr
+virDomainMemDevDefParseXML(xmlNodePtr node,
+ xmlXPathContextPtr ctxt)
+{
+ char *type;
+ virDomainMemDevDefPtr def;
+ xmlNodePtr save = ctxt->node;
+ char *tmp = NULL;
+ char *policy = NULL;
+
+ if (VIR_ALLOC(def) < 0)
+ return NULL;
+
+ type = virXMLPropString(node, "type");
+ if (type == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("memory device must contain a type name"));
+ goto error;
+ }
+
+ if ((def->type = virDomainMemDevTypeFromString(type)) < 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("unknown memory device type '%s'"), type);
+ goto error;
+ }
+
+ if ((tmp = virXMLPropString(node, "merge")) != NULL) {
+ if (STREQ(tmp, "yes"))
+ def->merge = true;
+ VIR_FREE(tmp);
+ }
+
+ if ((tmp = virXMLPropString(node, "dump")) != NULL) {
+ if (STREQ(tmp, "yes"))
+ def->dump = true;
+ VIR_FREE(tmp);
+ }
+
+ if ((tmp = virXMLPropString(node, "prealloc")) != NULL) {
+ if (STREQ(tmp, "yes"))
+ def->prealloc = true;
+ VIR_FREE(tmp);
+ }
+
+ ctxt->node = node;
+
+ def->name = virXPathString("string(./name)", ctxt);
+ if (!def->name) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing memory device name element"));
+ goto error;
+ }
+
+ /* Extract memory capacity */
+ if (virDomainParseMemory("./capacity", ctxt,
+ &def->capacity, true) < 0)
+ goto error;
+
+ def->mempath = virXPathString("string(./source/@mem-path)", ctxt);
+ if (def->type == VIR_DOMAIN_MEMDEV_FILE && !def->mempath) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("The mem-path element must be specified when "
+ "memory device type is 'file'"));
+ goto error;
+ }
+
+ if (def->type == VIR_DOMAIN_MEMDEV_RAM && def->mempath) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("The mem-path element should be not specified when "
+ "memory device type is 'ram'"));
+ goto error;
+ }
+
+ def->hostnodes = virXPathString("string(./source/@host-nodes)", ctxt);
+
+ policy = virXPathString("string(./source/@policy)", ctxt);
+ if (policy != NULL) {
+ if ((def->policy = virDomainHostNodePolicyTypeFromString(policy)) < 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("unknown host node policy: '%s'"),
policy);
+ goto error;
+ }
+ }
+
+cleanup:
+ VIR_FREE(type);
+ VIR_FREE(policy);
+ ctxt->node = save;
+
+ return def;
+
+ error:
+ virDomainMemDevDefFree(def);
+ def = NULL;
+ goto cleanup;
+}
+
+
static virDomainNVRAMDefPtr
virDomainNVRAMDefParseXML(xmlNodePtr node,
unsigned int flags)
@@ -10067,6 +10197,10 @@ virDomainDeviceDefParse(const char *xmlStr,
if (!(dev->data.nvram = virDomainNVRAMDefParseXML(node, flags)))
goto error;
break;
+ case VIR_DOMAIN_DEVICE_MEMDEV:
+ if (!(dev->data.memdev = virDomainMemDevDefParseXML(node, ctxt)))
+ goto error;
+ break;
case VIR_DOMAIN_DEVICE_NONE:
case VIR_DOMAIN_DEVICE_LAST:
break;
@@ -12752,6 +12886,24 @@ virDomainDefParseXML(xmlDocPtr xml,
}
VIR_FREE(nodes);
+ /* analysis of the memory devices */
+ if ((n = virXPathNodeSet("./devices/memdev", ctxt, &nodes)) < 0) {
+ goto error;
+ }
+
+ if (n && VIR_ALLOC_N(def->memdevs, n) < 0)
+ goto error;
+
+ for (i = 0; i < n; i++) {
+ virDomainMemDevDefPtr memdev = virDomainMemDevDefParseXML(nodes[i], ctxt);
+ if (!memdev)
+ goto error;
+
+ def->memdevs[def->nmemdevs++] = memdev;
+ }
+ VIR_FREE(nodes);
+
+
/* Parse the TPM devices */
if ((n = virXPathNodeSet("./devices/tpm", ctxt, &nodes)) < 0)
goto error;
@@ -16262,6 +16414,50 @@ virDomainTPMDefFormat(virBufferPtr buf,
return 0;
}
+static int
+virDomainMemDevDefFormat(virBufferPtr buf,
+ virDomainMemDevDefPtr def)
+{
+ const char *type = virDomainMemDevTypeToString(def->type);
+
+ if (!type) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unexpected memdev type %d"), def->type);
+ return -1;
+ }
+
+ virBufferAsprintf(buf, "<memdev type='%s'", type);
+ if (def->merge)
+ virBufferAddLit(buf, " merge='yes'");
+ if (def->dump)
+ virBufferAddLit(buf, " dump='yes'");
+ if (def->prealloc)
+ virBufferAddLit(buf, " prealloc='yes'");
+ virBufferAddLit(buf, ">\n");
+
+ virBufferAdjustIndent(buf, 2);
+
+ virBufferAsprintf(buf, "<name>%s</name>\n", def->name);
+ virBufferAsprintf(buf, "<capacity
unit='KiB'>%llu</capacity>\n",
+ def->capacity);
+
+ if (def->type == VIR_DOMAIN_MEMDEV_FILE || def->hostnodes || def->policy) {
+ virBufferAddLit(buf, "<source");
+ if (def->type == VIR_DOMAIN_MEMDEV_FILE)
+ virBufferAsprintf(buf, " mem-path='%s'", def->mempath);
+ if (def->hostnodes)
+ virBufferAsprintf(buf, " host-nodes='%s'",
def->hostnodes);
+ if (def->policy)
+ virBufferAsprintf(buf, " policy='%s'",
+ virDomainHostNodePolicyTypeToString(def->policy));
+ virBufferAddLit(buf, "/>\n");
+ }
+
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</memdev>\n");
+
+ return 0;
+}
static int
virDomainSoundDefFormat(virBufferPtr buf,
@@ -17916,6 +18112,10 @@ virDomainDefFormatInternal(virDomainDefPtr def,
if (def->memballoon)
virDomainMemballoonDefFormat(buf, def->memballoon, flags);
+ for (n = 0; n < def->nmemdevs; n++)
+ if (virDomainMemDevDefFormat(buf, def->memdevs[n]) < 0)
+ goto error;
+
if (def->rng)
virDomainRNGDefFormat(buf, def->rng, flags);
@@ -19291,6 +19491,7 @@ virDomainDeviceDefCopy(virDomainDeviceDefPtr src,
case VIR_DOMAIN_DEVICE_NONE:
case VIR_DOMAIN_DEVICE_SMARTCARD:
case VIR_DOMAIN_DEVICE_MEMBALLOON:
+ case VIR_DOMAIN_DEVICE_MEMDEV:
case VIR_DOMAIN_DEVICE_NVRAM:
case VIR_DOMAIN_DEVICE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index a6ac95a..33e57bc 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -109,6 +109,9 @@ typedef virDomainChrDef *virDomainChrDefPtr;
typedef struct _virDomainMemballoonDef virDomainMemballoonDef;
typedef virDomainMemballoonDef *virDomainMemballoonDefPtr;
+typedef struct _virDomainMemDevDef virDomainMemDevDef;
+typedef virDomainMemDevDef *virDomainMemDevDefPtr;
+
typedef struct _virDomainNVRAMDef virDomainNVRAMDef;
typedef virDomainNVRAMDef *virDomainNVRAMDefPtr;
@@ -151,6 +154,7 @@ typedef enum {
VIR_DOMAIN_DEVICE_MEMBALLOON,
VIR_DOMAIN_DEVICE_NVRAM,
VIR_DOMAIN_DEVICE_RNG,
+ VIR_DOMAIN_DEVICE_MEMDEV,
VIR_DOMAIN_DEVICE_LAST
} virDomainDeviceType;
@@ -178,6 +182,7 @@ struct _virDomainDeviceDef {
virDomainMemballoonDefPtr memballoon;
virDomainNVRAMDefPtr nvram;
virDomainRNGDefPtr rng;
+ virDomainMemDevDefPtr memdev;
} data;
};
@@ -1487,6 +1492,37 @@ struct _virDomainNVRAMDef {
virDomainDeviceInfo info;
};
+enum {
+ VIR_DOMAIN_MEMDEV_RAM,
+ VIR_DOMAIN_MEMDEV_FILE,
+
+ VIR_DOMAIN_MEMDEV_LAST
+};
+
+struct _virDomainMemDevDef {
+ char *name;
+ int type;
+
+ unsigned long long capacity; /* bytes */
+
+ bool merge;
+ bool dump;
+ bool prealloc;
+
+ char *mempath;
+ char *hostnodes;
+ int policy;
+};
+
+enum {
+ VIR_DOMAIN_HOST_NODE_POLICY_DEFAULT = 0,
+ VIR_DOMAIN_HOST_NODE_POLICY_PREFERRED,
+ VIR_DOMAIN_HOST_NODE_POLICY_BIND,
+ VIR_DOMAIN_HOST_NODE_POLICY_INTERLEAVE,
+
+ VIR_DOMAIN_HOST_NODE_POLICY_LAST
+};
+
typedef enum {
VIR_DOMAIN_SMBIOS_NONE = 0,
VIR_DOMAIN_SMBIOS_EMULATE,
@@ -1970,6 +2006,9 @@ struct _virDomainDef {
size_t nseclabels;
virSecurityLabelDefPtr *seclabels;
+ size_t nmemdevs;
+ virDomainMemDevDefPtr *memdevs;
+
/* Only 1 */
virDomainWatchdogDefPtr watchdog;
virDomainMemballoonDefPtr memballoon;
@@ -2159,6 +2198,7 @@ int virDomainChrSourceDefCopy(virDomainChrSourceDefPtr src,
void virDomainSoundCodecDefFree(virDomainSoundCodecDefPtr def);
void virDomainSoundDefFree(virDomainSoundDefPtr def);
void virDomainMemballoonDefFree(virDomainMemballoonDefPtr def);
+void virDomainMemDevDefFree(virDomainMemDevDefPtr def);
void virDomainNVRAMDefFree(virDomainNVRAMDefPtr def);
void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def);
void virDomainVideoDefFree(virDomainVideoDefPtr def);
@@ -2575,6 +2615,8 @@ VIR_ENUM_DECL(virDomainSoundCodec)
VIR_ENUM_DECL(virDomainSoundModel)
VIR_ENUM_DECL(virDomainMemDump)
VIR_ENUM_DECL(virDomainMemballoonModel)
+VIR_ENUM_DECL(virDomainMemDev)
+VIR_ENUM_DECL(virDomainHostNodePolicy)
VIR_ENUM_DECL(virDomainSmbiosMode)
VIR_ENUM_DECL(virDomainWatchdogModel)
VIR_ENUM_DECL(virDomainWatchdogAction)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 122c572..06d6d7f 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -293,6 +293,8 @@ virDomainHostdevModeTypeToString;
virDomainHostdevRemove;
virDomainHostdevSubsysPCIBackendTypeToString;
virDomainHostdevSubsysTypeToString;
+virDomainHostNodePolicyTypeFromString;
+virDomainHostNodePolicyTypeToString;
virDomainHubTypeFromString;
virDomainHubTypeToString;
virDomainHypervTypeFromString;
@@ -316,6 +318,8 @@ virDomainLockFailureTypeFromString;
virDomainLockFailureTypeToString;
virDomainMemballoonModelTypeFromString;
virDomainMemballoonModelTypeToString;
+virDomainMemDevTypeFromString;
+virDomainMemDevTypeToString;
virDomainMemDumpTypeFromString;
virDomainMemDumpTypeToString;
virDomainNetDefFormat;
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 08c3d04..718da2b 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -256,6 +256,8 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST,
"usb-kbd", /* 165 */
"host-pci-multidomain",
"msg-timestamp",
+ "memory-backend-ram",
+ "memory-backend-file",
);
@@ -1440,6 +1442,8 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = {
{ "ich9-intel-hda", QEMU_CAPS_DEVICE_ICH9_INTEL_HDA },
{ "pvpanic", QEMU_CAPS_DEVICE_PANIC },
{ "usb-kbd", QEMU_CAPS_DEVICE_USB_KBD },
+ { "memory-backend-ram", QEMU_CAPS_MEMORY_BACKEND_RAM },
+ { "memory-backend-file", QEMU_CAPS_MEMORY_BACKEND_FILE },
};
static struct virQEMUCapsStringFlags virQEMUCapsObjectPropsVirtioBlk[] = {
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index d755caa..53171be 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -206,6 +206,8 @@ enum virQEMUCapsFlags {
QEMU_CAPS_DEVICE_USB_KBD = 165, /* -device usb-kbd */
QEMU_CAPS_HOST_PCI_MULTIDOMAIN = 166, /* support domain > 0 in host pci address
*/
QEMU_CAPS_MSG_TIMESTAMP = 167, /* -msg timestamp */
+ QEMU_CAPS_MEMORY_BACKEND_RAM = 168, /* -object memory-backend-ram */
+ QEMU_CAPS_MEMORY_BACKEND_FILE = 169, /* -object memory-backend-file */
QEMU_CAPS_LAST, /* this must always be the last item */
};
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index b351f60..25a6e00 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -4743,6 +4743,71 @@ qemuBuildNVRAMDevStr(virDomainNVRAMDefPtr dev)
}
char *
+qemuBuildMemObjectStr(virDomainMemDevDefPtr dev,
+ virQEMUCapsPtr qemuCaps)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ switch (dev->type) {
+ case VIR_DOMAIN_MEMDEV_RAM:
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MEMORY_BACKEND_RAM)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("The object memory-backend-ram "
+ "is not supported in this QEMU binary"));
+ goto error;
+ }
+ virBufferAddLit(&buf, "memory-backend-ram");
+ break;
+ case VIR_DOMAIN_MEMDEV_FILE:
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MEMORY_BACKEND_FILE)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("The object memory-backend-file "
+ "is not supported in this QEMU binary"));
+ goto error;
+ }
+ virBufferAddLit(&buf, "memory-backend-file");
+ break;
+ default:
+ virReportError(VIR_ERR_XML_ERROR,
+ _("memdev unsupported with type '%s'"),
+ virDomainMemDevTypeToString(dev->type));
+ goto error;
+ }
+ virBufferAsprintf(&buf, ",id=%s", dev->name);
+ dev->capacity = VIR_DIV_UP(dev->capacity, 1024) * 1024;
+ virBufferAsprintf(&buf, ",size=%lluK", dev->capacity);
+
+ if (dev->merge)
+ virBufferAddLit(&buf, ",merge=yes");
+ if (dev->dump)
+ virBufferAddLit(&buf, ",dump=yes");
+ if (dev->prealloc)
+ virBufferAddLit(&buf, ",prealloc=yes");
+
+ if (dev->type == VIR_DOMAIN_MEMDEV_FILE && dev->mempath)
+ virBufferAsprintf(&buf, ",mem-path=%s", dev->mempath);
+
+ if (dev->hostnodes)
+ virBufferAsprintf(&buf, ",host-nodes=%s", dev->hostnodes);
+
+ if (dev->policy)
+ virBufferAsprintf(&buf, ",policy=%s",
+ virDomainHostNodePolicyTypeToString(dev->policy));
+
+ if (virBufferError(&buf)) {
+ virReportOOMError();
+ goto error;
+ }
+
+ return virBufferContentAndReset(&buf);
+
+error:
+ virBufferFreeAndReset(&buf);
+ return NULL;
+}
+
+
+char *
qemuBuildUSBInputDevStr(virDomainDefPtr def,
virDomainInputDefPtr dev,
virQEMUCapsPtr qemuCaps)
@@ -7394,6 +7459,15 @@ qemuBuildCommandLine(virConnectPtr conn,
}
mlock = def->mem.locked;
+ for (i = 0; i < def->nmemdevs; i++) {
+ char *objectstr;
+ virDomainMemDevDefPtr memptr = def->memdevs[i];
+
+ if (!(objectstr = qemuBuildMemObjectStr(memptr, qemuCaps)))
+ goto error;
+ virCommandAddArgList(cmd, "-object", objectstr, NULL);
+ }
+
virCommandAddArg(cmd, "-smp");
if (!(smp = qemuBuildSmpArgStr(def, qemuCaps)))
goto error;
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index afbd6ff..72f4e77 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -274,4 +274,8 @@ qemuParseKeywords(const char *str,
int qemuGetDriveSourceString(virStorageSourcePtr src,
virConnectPtr conn,
char **source);
+
+char *
+qemuBuildMemObjectStr(virDomainMemDevDefPtr dev,
+ virQEMUCapsPtr qemuCaps);
#endif /* __QEMU_COMMAND_H__*/
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 7289055..c9f4df9 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -2818,6 +2818,7 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver,
case VIR_DOMAIN_DEVICE_MEMBALLOON:
case VIR_DOMAIN_DEVICE_NVRAM:
case VIR_DOMAIN_DEVICE_RNG:
+ case VIR_DOMAIN_DEVICE_MEMDEV:
case VIR_DOMAIN_DEVICE_LAST:
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
_("don't know how to remove a %s device"),
--
1.9.3