single host block device, if they are backed by files within
the same host file system, which is why this tuning parameter
is at the global domain level rather than associated with each
- guest disk device. Each <code>device</code> element has two
+ guest disk device (contrast this to
+ the <a href="#elementsDisks"><code><iotune></code></a>
+ element which can apply to an
+ individual <code><disk></code>).
+ Each <code>device</code> element has two
mandatory sub-elements, <code>path</code> describing the
absolute path of the device, and <code>weight</code> giving
the relative weight of that device, in the range [100,
<driver name="tap" type="aio" cache="default"/>
<source file='/var/lib/xen/images/fv0'/ startupPolicy='optional'>
<target dev='hda' bus='ide'/>
+ <iotune>
+ <total_bytes_sec>10000000</total_bytes_sec>
+ <read_iops_sec>400000</read_iops_sec>
+ <write_iops_sec>100000</write_iops_sec>
+ </iotune>
<boot order='2'/>
<encryption type='...'>
...
<span class="since">Since 0.0.3; <code>bus</code> attribute since 0.4.3;
"usb" attribute value since after 0.4.4; "sata" attribute value since
0.9.7</span></dd>
+ <dt><code>iotune</code></dt>
+ <dd>The optional <code>iotune</code> element provides the
+ ability to provide additional per-device I/O tuning, with
+ values that can vary for each device (contrast this to
+ the <a href="#elementsBlockTuning"><code><blkiotune></code></a>
+ element, which applies globally to the domain). Currently,
+ the only tuning available is Block I/O throttling for qemu.
+ This element has optional sub-elements; any sub-element not
+ specified or given with a value of 0 implies no
+ limit. <span class="since">Since 0.9.8</span>
+ <dl>
+ <dt><code>total_bytes_sec</code></dt>
+ <dd>The optional <code>total_bytes_sec</code> element is the
+ total throughput limit in bytes per second. This cannot
+ appear with <code>read_bytes_sec</code>
+ or <code>write_bytes_sec</code>.</dd>
+ <dt><code>read_bytes_sec</code></dt>
+ <dd>The optional <code>read_bytes_sec</code> element is the
+ read throughput limit in bytes per second.</dd>
+ <dt><code>write_bytes_sec</code></dt>
+ <dd>The optional <code>write_bytes_sec</code> element is the
+ write throughput limit in bytes per second.</dd>
+ <dt><code>total_iops_sec</code></dt>
+ <dd>The optional <code>total_iops_sec</code> element is the
+ total I/O operations per second. This cannot
+ appear with <code>read_iops_sec</code>
+ or <code>write_iops_sec</code>.</dd>
+ <dt><code>read_iops_sec</code></dt>
+ <dd>The optional <code>read_iops_sec</code> element is the
+ read I/O operations per second.</dd>
+ <dt><code>write_iops_sec</code></dt>
+ <dd>The optional <code>write_iops_sec</code> element is the
+ write I/O operations per second.</dd>
+ </dl>
<dt><code>driver</code></dt>
<dd>
The optional driver element allows specifying further details
</element>
</define>
<define name="diskspec">
- <optional>
- <ref name="driver"/>
- </optional>
- <optional>
- <ref name="diskAuth"/>
- </optional>
- <ref name="target"/>
- <optional>
- <ref name="deviceBoot"/>
- </optional>
- <optional>
- <element name="readonly">
- <empty/>
- </element>
- </optional>
- <optional>
- <element name="shareable">
- <empty/>
- </element>
- </optional>
- <optional>
- <element name="transient">
- <empty/>
- </element>
- </optional>
- <optional>
- <element name="serial">
- <ref name="diskSerial"/>
- </element>
- </optional>
- <optional>
- <ref name="encryption"/>
- </optional>
- <optional>
- <ref name="address"/>
- </optional>
+ <interleave>
+ <optional>
+ <ref name="driver"/>
+ </optional>
+ <optional>
+ <ref name="diskAuth"/>
+ </optional>
+ <ref name="target"/>
+ <optional>
+ <ref name="deviceBoot"/>
+ </optional>
+ <optional>
+ <element name="readonly">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="shareable">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="transient">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="serial">
+ <ref name="diskSerial"/>
+ </element>
+ </optional>
+ <optional>
+ <ref name="encryption"/>
+ </optional>
+ <optional>
+ <ref name="diskIoTune"/>
+ </optional>
+ <optional>
+ <ref name="address"/>
+ </optional>
+ </interleave>
</define>
<define name="snapshot">
<attribute name="snapshot">
</element>
</define>
+ <define name='diskIoTune'>
+ <element name="iotune">
+ <interleave>
+ <choice>
+ <element name="total_bytes_sec">
+ <data type="unsignedLong"/>
+ </element>
+ <group>
+ <interleave>
+ <optional>
+ <element name="read_bytes_sec">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="write_bytes_sec">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ <choice>
+ <element name="total_iops_sec">
+ <data type="unsignedLong"/>
+ </element>
+ <group>
+ <interleave>
+ <optional>
+ <element name="read_iops_sec">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ <optional>
+ <element name="write_iops_sec">
+ <data type="unsignedLong"/>
+ </element>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ </interleave>
+ </element>
+ </define>
+
<!--
Optional hypervisor extensions in their own namespace:
QEmu
static virDomainDiskDefPtr
virDomainDiskDefParseXML(virCapsPtr caps,
xmlNodePtr node,
+ xmlXPathContextPtr ctxt,
virBitmapPtr bootMap,
unsigned int flags)
{
virDomainDiskDefPtr def;
xmlNodePtr cur, child;
+ xmlNodePtr save_ctxt = ctxt->node;
char *type = NULL;
char *device = NULL;
char *snapshot = NULL;
return NULL;
}
+ ctxt->node = node;
+
type = virXMLPropString(node, "type");
if (type) {
if ((def->type = virDomainDiskTypeFromString(type)) < 0) {
}
child = child->next;
}
+ } else if (xmlStrEqual(cur->name, BAD_CAST "iotune")) {
+ if (virXPathULongLong("string(./iotune/total_bytes_sec)",
+ ctxt,
+ &def->blkdeviotune.total_bytes_sec) < 0) {
+ def->blkdeviotune.total_bytes_sec = 0;
+ }
+
+ if (virXPathULongLong("string(./iotune/read_bytes_sec)",
+ ctxt,
+ &def->blkdeviotune.read_bytes_sec) < 0) {
+ def->blkdeviotune.read_bytes_sec = 0;
+ }
+
+ if (virXPathULongLong("string(./iotune/write_bytes_sec)",
+ ctxt,
+ &def->blkdeviotune.write_bytes_sec) < 0) {
+ def->blkdeviotune.write_bytes_sec = 0;
+ }
+
+ if (virXPathULongLong("string(./iotune/total_iops_sec)",
+ ctxt,
+ &def->blkdeviotune.total_iops_sec) < 0) {
+ def->blkdeviotune.total_iops_sec = 0;
+ }
+
+ if (virXPathULongLong("string(./iotune/read_iops_sec)",
+ ctxt,
+ &def->blkdeviotune.read_iops_sec) < 0) {
+ def->blkdeviotune.read_iops_sec = 0;
+ }
+
+ if (virXPathULongLong("string(./iotune/write_iops_sec)",
+ ctxt,
+ &def->blkdeviotune.write_iops_sec) < 0) {
+ def->blkdeviotune.write_iops_sec = 0;
+ }
+
+ if ((def->blkdeviotune.total_bytes_sec &&
+ def->blkdeviotune.read_bytes_sec) ||
+ (def->blkdeviotune.total_bytes_sec &&
+ def->blkdeviotune.write_bytes_sec)) {
+ virDomainReportError(VIR_ERR_XML_ERROR,
+ _("total and read/write bytes_sec "
+ "cannot be set at the same time"));
+ goto error;
+ }
+
+ if ((def->blkdeviotune.total_iops_sec &&
+ def->blkdeviotune.read_iops_sec) ||
+ (def->blkdeviotune.total_iops_sec &&
+ def->blkdeviotune.write_iops_sec)) {
+ virDomainReportError(VIR_ERR_XML_ERROR,
+ _("total and read/write iops_sec "
+ "cannot be set at the same time"));
+ goto error;
+ }
} else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) {
def->readonly = 1;
} else if (xmlStrEqual(cur->name, BAD_CAST "shareable")) {
virStorageEncryptionFree(encryption);
VIR_FREE(startupPolicy);
+ ctxt->node = save_ctxt;
return def;
no_memory:
if (xmlStrEqual(node->name, BAD_CAST "disk")) {
dev->type = VIR_DOMAIN_DEVICE_DISK;
- if (!(dev->data.disk = virDomainDiskDefParseXML(caps, node,
+ if (!(dev->data.disk = virDomainDiskDefParseXML(caps, node, ctxt,
NULL, flags)))
goto error;
} else if (xmlStrEqual(node->name, BAD_CAST "lease")) {
for (i = 0 ; i < n ; i++) {
virDomainDiskDefPtr disk = virDomainDiskDefParseXML(caps,
nodes[i],
+ ctxt,
bootMap,
flags);
if (!disk)
virBufferAsprintf(buf, " <target dev='%s' bus='%s'/>\n",
def->dst, bus);
+ /*disk I/O throttling*/
+ if (def->blkdeviotune.total_bytes_sec ||
+ def->blkdeviotune.read_bytes_sec ||
+ def->blkdeviotune.write_bytes_sec ||
+ def->blkdeviotune.total_iops_sec ||
+ def->blkdeviotune.read_iops_sec ||
+ def->blkdeviotune.write_iops_sec) {
+ virBufferAddLit(buf, " <iotune>\n");
+ if (def->blkdeviotune.total_bytes_sec) {
+ virBufferAsprintf(buf, " <total_bytes_sec>%llu</total_bytes_sec>\n",
+ def->blkdeviotune.total_bytes_sec);
+ }
+
+ if (def->blkdeviotune.read_bytes_sec) {
+ virBufferAsprintf(buf, " <read_bytes_sec>%llu</read_bytes_sec>\n",
+ def->blkdeviotune.read_bytes_sec);
+
+ }
+
+ if (def->blkdeviotune.write_bytes_sec) {
+ virBufferAsprintf(buf, " <write_bytes_sec>%llu</write_bytes_sec>\n",
+ def->blkdeviotune.write_bytes_sec);
+ }
+
+ if (def->blkdeviotune.total_iops_sec) {
+ virBufferAsprintf(buf, " <total_iops_sec>%llu</total_iops_sec>\n",
+ def->blkdeviotune.total_iops_sec);
+ }
+
+ if (def->blkdeviotune.read_iops_sec) {
+ virBufferAsprintf(buf, " <read_iops_sec>%llu</read_iops_sec>",
+ def->blkdeviotune.read_iops_sec);
+ }
+
+ if (def->blkdeviotune.write_iops_sec) {
+ virBufferAsprintf(buf, " <write_iops_sec>%llu</write_iops_sec>",
+ def->blkdeviotune.write_iops_sec);
+ }
+
+ virBufferAddLit(buf, " </iotune>\n");
+ }
+
if (def->bootIndex)
virBufferAsprintf(buf, " <boot order='%d'/>\n", def->bootIndex);
if (def->readonly)
VIR_DOMAIN_DISK_SECRET_TYPE_LAST
};
+typedef struct _virDomainBlockIoTuneInfo virDomainBlockIoTuneInfo;
+struct _virDomainBlockIoTuneInfo {
+ unsigned long long total_bytes_sec;
+ unsigned long long read_bytes_sec;
+ unsigned long long write_bytes_sec;
+ unsigned long long total_iops_sec;
+ unsigned long long read_iops_sec;
+ unsigned long long write_iops_sec;
+};
+typedef virDomainBlockIoTuneInfo *virDomainBlockIoTuneInfoPtr;
+
/* Stores the virtual disk configuration */
typedef struct _virDomainDiskDef virDomainDiskDef;
typedef virDomainDiskDef *virDomainDiskDefPtr;
} auth;
char *driverName;
char *driverType;
+
+ virDomainBlockIoTuneInfo blkdeviotune;
+
char *serial;
int cachemode;
int error_policy; /* enum virDomainDiskErrorPolicy */