storage: Introduce parentaddr into virStoragePoolSourceAdapter

Between reboots and kernel reloads, the SCSI host number used for SCSI
storage pools may change requiring modification to the storage pool XML
in order to use a specific SCSI host adapter.

This patch introduces the "parentaddr" element and "unique_id" attribute
for the SCSI host adapter in order to uniquely identify the adapter
between reboots and kernel reloads. For now the goal is to only parse
and format the XML. Both will be required to be provided in order to
uniquely identify the desired SCSI host.

The new XML is expected to be as follows:

  <adapter type='scsi_host'>
    <parentaddr unique_id='3'>
      <address domain='0x0000' bus='0x00' slot='0x1f' func='0x2'/>
    </parentaddr>
  </adapter>

where "parentaddr" is the parent device of the SCSI host using the PCI
address on which the device resides and the value from the unique_id file
for the device. Both the PCI address and unique_id values will be used
to traverse the /sys/class/scsi_host/ directories looking at each link
to match the PCI address reformatted to the directory link format where
"domain🚌slot:function" is found.  Then for each matching directory
the unique_id file for the scsi_host will be used to match the unique_id
value in the xml.

For a PCI address listed above, this will be formatted to "0000:00:1f.2"
and the links in /sys/class/scsi_host will be used to find the host#
to be used for the 'scsi_host' device. Each entry is a link to the
/sys/bus/pci/devices directories, e.g.:

%  ls -al /sys/class/scsi_host/host2
lrwxrwxrwx. 1 root root 0 Jun  1 00:22 /sys/class/scsi_host/host2 -> ../../devices/pci0000:00/0000:00:1f.2/ata3/host2/scsi_host/host2

% cat /sys/class/scsi_host/host2/unique_id
3

The "parentaddr" and "name" attributes are mutually exclusive to identify
the SCSI host number. Use of the "parentaddr" element will be the preferred
mechanism.

This patch only supports to parse and format the XMLs. Later patches will
add code to find out the scsi host number.
This commit is contained in:
Osier Yang 2014-03-03 22:15:13 -05:00 committed by John Ferlan
parent 53f620568e
commit a4bd62adc1
7 changed files with 285 additions and 31 deletions

View File

@ -86,6 +86,24 @@
&lt;/source&gt; &lt;/source&gt;
...</pre> ...</pre>
<pre>
...
&lt;source&gt;
&lt;adapter type='scsi_host' name='scsi_host1'/&gt;
&lt;/source&gt;
...</pre>
<pre>
...
&lt;source&gt;
&lt;adapter type='scsi_host'&gt;
&lt;parentaddr unique_id='1'&gt;
&lt;address domain='0x0000' bus='0x00' slot='0x1f' addr='0x2'/&gt;
&lt;/parentaddr&gt;
&lt;/adapter&gt;
&lt;/source&gt;
...</pre>
<pre> <pre>
... ...
&lt;source&gt; &lt;source&gt;
@ -111,25 +129,109 @@
<span class="since">Since 0.4.1</span></dd> <span class="since">Since 0.4.1</span></dd>
<dt><code>adapter</code></dt> <dt><code>adapter</code></dt>
<dd>Provides the source for pools backed by SCSI adapters (pool <dd>Provides the source for pools backed by SCSI adapters (pool
type <code>scsi</code>). May type <code>scsi</code>). May only occur once.
only occur once. Attribute <code>name</code> is the SCSI adapter <dl>
name (ex. "scsi_host1". NB, although a name such as "host1" is <dt><code>name</code></dt>
still supported for backwards compatibility, it is not recommended). <dd>The SCSI adapter name (e.g. "scsi_host1", although a name
Attribute <code>type</code> (<span class="since">1.0.5</span>) such as "host1" is still supported for backwards compatibility,
specifies the adapter type. Valid values are "fc_host" and "scsi_host". it is not recommended). The scsi_host name to be used can be
If omitted and the <code>name</code> attribute is specified, then it determined from the output of a <code>virsh nodedev-list
defaults to "scsi_host". To keep backwards compatibility, the attribute scsi_host</code> command followed by a combination of
<code>type</code> is optional for the "scsi_host" adapter, but <code>lspci</code> and <code>virsh nodedev-dumpxml
mandatory for the "fc_host" adapter. Attributes <code>wwnn</code> scsi_hostN</code> commands to find the <code>scsi_hostN</code>
(Word Wide Node Name) and <code>wwpn</code> (Word Wide Port Name) to be used. <span class="since">Since 0.6.2</span>
(<span class="since">1.0.4</span>) are used by the "fc_host" adapter <p>
It is further recommended to utilize the
<code>parentaddr</code> element since it's possible to have
the path to which the scsi_hostN uses change between system
reboots. <span class="since">Since 1.2.7</span>
</p>
</dd>
</dl>
<dl>
<dt><code>type</code></dt>
<dd>Specifies the adapter type. Valid values are "scsi_host" or
"fc_host". If omitted and the <code>name</code> attribute is
specified, then it defaults to "scsi_host". To keep backwards
compatibility, this attribute is optional <b>only</b> for the
"scsi_host" adapter, but is mandatory for the "fc_host" adapter.
<span class="since">Since 1.0.5</span>
</dd>
</dl>
<dl>
<dt><code>wwwn</code> and <code>wwpn</code></dt>
<dd>The "World Wide Node Name" (<code>wwnn</code>) and "World Wide
Port Name" (<code>wwpn</code>) are used by the "fc_host" adapter
to uniquely identify the device in the Fibre Channel storage fabric to uniquely identify the device in the Fibre Channel storage fabric
(the device can be either a HBA or vHBA). Both wwnn and wwpn should (the device can be either a HBA or vHBA). Both wwnn and wwpn should
be specified (See command 'virsh nodedev-dumpxml' to known how to get be specified. Use the command 'virsh nodedev-dumpxml' to determine
wwnn/wwpn of a (v)HBA). The optional attribute <code>parent</code> how to set the values for the wwnn/wwpn of a (v)HBA.
(<span class="since">1.0.4</span>) specifies the parent device for <span class="since">Since 1.0.4</span>
the "fc_host" adapter. </dd>
<span class="since">Since 0.6.2</span></dd> </dl>
<dl>
<dt><code>parent</code></dt>
<dd>Used by the "fc_host" adapter type to optionally specify the
parent scsi_host device defined in the
<a href="formatnode.html">Node Device</a> database as the
<a href="http://wiki.libvirt.org/page/NPIV_in_libvirt">NPIV</a>
virtual Host Bus Adapter (vHBA).
<span class="since">Since 1.0.4</span>
</dd>
</dl>
<dl>
<dt><code>parentaddr</code></dt>
<dd>Used by the "scsi_host" adapter type instead of the
<code>name</code> attribute to more uniquely identify the
SCSI host. Using a combination of the <code>unique_id</code>
attribute and the <code>address</code> element to formulate
a PCI address, a search will be performed of the
<code>/sys/class/scsi_host/hostNN</code> links for a
matching PCI address with a matching <code>unique_id</code>
value in the <code>/sys/class/scsi_host/hostNN/unique_id</code>
file. The value in the "unique_id" file will be unique enough
for the specific PCI address. The <code>hostNN</code> will be
used by libvirt as the basis to define which SCSI host is to
be used for the currently booted system.
<span class="since">Since 1.2.7</span>
<dl>
<dt><code>address</code></dt>
<dd>The PCI address of the scsi_host device to be used. Using
a PCI address provides consistent naming across system reboots
and kernel reloads. The address will have four attributes:
<code>domain</code> (a 2-byte hex integer, not currently used
by qemu), <code>bus</code> (a hex value between 0 and 0xff,
inclusive), <code>slot</code> (a hex value between 0x0 and
0x1f, inclusive), and <code>function</code> (a value between
0 and 7, inclusive). The PCI address can be determined by
listing the <code>/sys/bus/pci/devices</code> and the
<code>/sys/class/scsi_host</code> directories in order to
find the expected scsi_host device. The address will be
provided in a format such as "0000:00:1f:2" which can be
used to generate the expected PCI address
"domain='0x0000' bus='0x00' slot='0x1f' function='0x0'".
Optionally, using the combination of the commands 'virsh
nodedev-list scsi_host' and 'virsh nodedev-dumpxml' for a
specific list entry and converting the resulting
<code>path</code> element as the basis to formulate the
correctly formatted PCI address.
</dd>
</dl>
<dl>
<dt><code>unique_id</code></dt>
<dd>Required <code>parentaddr</code> attribute used to determine
which of the scsi_host adapters for the provided PCI address
should be used. The value is determine by contents of the
<code>unique_id</code> file for the specific scsi_host adapter.
For a PCI address of "0000:00:1f:2", the unique identifer files
can be found using the command
<code>find -H /sys/class/scsi_host/host*/unique_id |
xargs grep '[0-9]'</code>.
</dd>
</dl>
</dd>
</dl>
</dd>
<dt><code>host</code></dt> <dt><code>host</code></dt>
<dd>Provides the source for pools backed by storage from a <dd>Provides the source for pools backed by storage from a
remote server (pool types <code>netfs</code>, <code>iscsi</code>, remote server (pool types <code>netfs</code>, <code>iscsi</code>,

View File

@ -361,10 +361,28 @@
<value>scsi_host</value> <value>scsi_host</value>
</attribute> </attribute>
</optional> </optional>
<choice>
<group>
<attribute name='name'> <attribute name='name'>
<text/> <text/>
</attribute> </attribute>
</group> </group>
<group>
<interleave>
<element name="parentaddr">
<optional>
<attribute name='unique_id'>
<ref name='positiveInteger'/>
</attribute>
</optional>
<element name="address">
<ref name="pciaddress"/>
</element>
</element>
</interleave>
</group>
</choice>
</group>
<group> <group>
<attribute name='type'> <attribute name='type'>
<value>fc_host</value> <value>fc_host</value>

View File

@ -576,14 +576,43 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
virXPathString("string(./adapter/@wwpn)", ctxt); virXPathString("string(./adapter/@wwpn)", ctxt);
} else if (source->adapter.type == } else if (source->adapter.type ==
VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) {
source->adapter.data.scsi_host.name = source->adapter.data.scsi_host.name =
virXPathString("string(./adapter/@name)", ctxt); virXPathString("string(./adapter/@name)", ctxt);
if (virXPathNode("./adapter/parentaddr", ctxt)) {
xmlNodePtr addrnode = virXPathNode("./adapter/parentaddr/address",
ctxt);
virDevicePCIAddressPtr addr =
&source->adapter.data.scsi_host.parentaddr;
if (!addrnode) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Missing scsi_host PCI address element"));
goto cleanup;
}
source->adapter.data.scsi_host.has_parent = true;
if (virDevicePCIAddressParseXML(addrnode, addr) < 0)
goto cleanup;
if ((virXPathInt("string(./adapter/parentaddr/@unique_id)",
ctxt,
&source->adapter.data.scsi_host.unique_id) < 0) ||
(source->adapter.data.scsi_host.unique_id < 0)) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Missing or invalid scsi adapter "
"'unique_id' value"));
goto cleanup;
}
}
} }
} else { } else {
char *wwnn = NULL; char *wwnn = NULL;
char *wwpn = NULL; char *wwpn = NULL;
char *parent = NULL; char *parent = NULL;
/* "type" was not specified in the XML, so we must verify that
* "wwnn", "wwpn", "parent", or "parentaddr" are also not in the
* XML. If any are found, then we cannot just use "name" alone".
*/
wwnn = virXPathString("string(./adapter/@wwnn)", ctxt); wwnn = virXPathString("string(./adapter/@wwnn)", ctxt);
wwpn = virXPathString("string(./adapter/@wwpn)", ctxt); wwpn = virXPathString("string(./adapter/@wwpn)", ctxt);
parent = virXPathString("string(./adapter/@parent)", ctxt); parent = virXPathString("string(./adapter/@parent)", ctxt);
@ -594,7 +623,14 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
VIR_FREE(parent); VIR_FREE(parent);
virReportError(VIR_ERR_XML_ERROR, "%s", virReportError(VIR_ERR_XML_ERROR, "%s",
_("Use of 'wwnn', 'wwpn', and 'parent' attributes " _("Use of 'wwnn', 'wwpn', and 'parent' attributes "
"requires the 'fc_host' adapter 'type'")); "requires use of the adapter 'type'"));
goto cleanup;
}
if (virXPathNode("./adapter/parentaddr", ctxt)) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Use of 'parent' element requires use "
"of the adapter 'type'"));
goto cleanup; goto cleanup;
} }
@ -854,9 +890,19 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt)
goto error; goto error;
} else if (ret->source.adapter.type == } else if (ret->source.adapter.type ==
VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) {
if (!ret->source.adapter.data.scsi_host.name) { if (!ret->source.adapter.data.scsi_host.name &&
!ret->source.adapter.data.scsi_host.has_parent) {
virReportError(VIR_ERR_XML_ERROR, "%s", virReportError(VIR_ERR_XML_ERROR, "%s",
_("missing storage pool source adapter name")); _("Either 'name' or 'parent' must be specified "
"for the 'scsi_host' adapter"));
goto error;
}
if (ret->source.adapter.data.scsi_host.name &&
ret->source.adapter.data.scsi_host.has_parent) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Both 'name' and 'parent' cannot be specified "
"for the 'scsi_host' adapter"));
goto error; goto error;
} }
} }
@ -1021,8 +1067,23 @@ virStoragePoolSourceFormat(virBufferPtr buf,
src->adapter.data.fchost.wwpn); src->adapter.data.fchost.wwpn);
} else if (src->adapter.type == } else if (src->adapter.type ==
VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) { VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) {
if (src->adapter.data.scsi_host.name) {
virBufferAsprintf(buf, " name='%s'/>\n", virBufferAsprintf(buf, " name='%s'/>\n",
src->adapter.data.scsi_host.name); src->adapter.data.scsi_host.name);
} else {
virDevicePCIAddress addr;
virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2);
virBufferAsprintf(buf, "<parentaddr unique_id='%d'>\n",
src->adapter.data.scsi_host.unique_id);
virBufferAdjustIndent(buf, 2);
addr = src->adapter.data.scsi_host.parentaddr;
ignore_value(virDevicePCIAddressFormat(buf, addr, false));
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</parentaddr>\n");
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</adapter>\n");
}
} }
} }
@ -1987,6 +2048,28 @@ virStoragePoolObjIsDuplicate(virStoragePoolObjListPtr pools,
return ret; return ret;
} }
static bool
matchSCSIAdapterParent(virStoragePoolObjPtr pool,
virStoragePoolDefPtr def)
{
virDevicePCIAddressPtr pooladdr =
&pool->def->source.adapter.data.scsi_host.parentaddr;
virDevicePCIAddressPtr defaddr =
&def->source.adapter.data.scsi_host.parentaddr;
int pool_unique_id =
pool->def->source.adapter.data.scsi_host.unique_id;
int def_unique_id =
def->source.adapter.data.scsi_host.unique_id;
if (pooladdr->domain == defaddr->domain &&
pooladdr->bus == defaddr->bus &&
pooladdr->slot == defaddr->slot &&
pooladdr->function == defaddr->function &&
pool_unique_id == def_unique_id) {
return true;
}
return false;
}
int int
virStoragePoolSourceFindDuplicate(virStoragePoolObjListPtr pools, virStoragePoolSourceFindDuplicate(virStoragePoolObjListPtr pools,
virStoragePoolDefPtr def) virStoragePoolDefPtr def)
@ -2029,9 +2112,14 @@ virStoragePoolSourceFindDuplicate(virStoragePoolObjListPtr pools,
matchpool = pool; matchpool = pool;
} else if (pool->def->source.adapter.type == } else if (pool->def->source.adapter.type ==
VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST){ VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST){
if (pool->def->source.adapter.data.scsi_host.name) {
if (STREQ(pool->def->source.adapter.data.scsi_host.name, if (STREQ(pool->def->source.adapter.data.scsi_host.name,
def->source.adapter.data.scsi_host.name)) def->source.adapter.data.scsi_host.name))
matchpool = pool; matchpool = pool;
} else {
if (matchSCSIAdapterParent(pool, def))
matchpool = pool;
}
} }
break; break;
case VIR_STORAGE_POOL_ISCSI: case VIR_STORAGE_POOL_ISCSI:

View File

@ -29,6 +29,7 @@
# include "virstoragefile.h" # include "virstoragefile.h"
# include "virbitmap.h" # include "virbitmap.h"
# include "virthread.h" # include "virthread.h"
# include "device_conf.h"
# include <libxml/tree.h> # include <libxml/tree.h>
@ -181,6 +182,9 @@ struct _virStoragePoolSourceAdapter {
union { union {
struct { struct {
char *name; char *name;
virDevicePCIAddress parentaddr; /* host address */
int unique_id;
bool has_parent;
} scsi_host; } scsi_host;
struct { struct {
char *parent; char *parent;

View File

@ -0,0 +1,19 @@
<pool type="scsi">
<name>hba0</name>
<uuid>e9392370-2917-565e-692b-d057f46512d6</uuid>
<source>
<adapter type='scsi_host'>
<parentaddr unique_id='5'>
<address domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
</parentaddr>
</adapter>
</source>
<target>
<path>/dev/disk/by-path</path>
<permissions>
<mode>0700</mode>
<owner>0</owner>
<group>0</group>
</permissions>
</target>
</pool>

View File

@ -0,0 +1,22 @@
<pool type='scsi'>
<name>hba0</name>
<uuid>e9392370-2917-565e-692b-d057f46512d6</uuid>
<capacity unit='bytes'>0</capacity>
<allocation unit='bytes'>0</allocation>
<available unit='bytes'>0</available>
<source>
<adapter type='scsi_host'>
<parentaddr unique_id='5'>
<address domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
</parentaddr>
</adapter>
</source>
<target>
<path>/dev/disk/by-path</path>
<permissions>
<mode>0700</mode>
<owner>0</owner>
<group>0</group>
</permissions>
</target>
</pool>

View File

@ -104,6 +104,7 @@ mymain(void)
DO_TEST("pool-sheepdog"); DO_TEST("pool-sheepdog");
DO_TEST("pool-gluster"); DO_TEST("pool-gluster");
DO_TEST("pool-gluster-sub"); DO_TEST("pool-gluster-sub");
DO_TEST("pool-scsi-type-scsi-host-stable");
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
} }