</optional>
</define>
+ <define name='capsfcrport'>
+ <attribute name='type'>
+ <value>fc_remote_port</value>
+ </attribute>
+
+ <element name='rport'>
+ <text/>
+ </element>
+
+ <element name='wwpn'>
+ <ref name='wwn'/>
+ </element>
+ </define>
+
<define name='capscsitarget'>
<attribute name='type'>
<value>scsi_target</value>
<element name='target'>
<text/>
</element>
+
+ <optional>
+ <element name='capability'>
+ <ref name='capsfcrport'/>
+ </element>
+ </optional>
</define>
<define name='capscsi'>
case VIR_NODE_DEV_CAP_SCSI_TARGET:
virBufferEscapeString(&buf, "<target>%s</target>\n",
data->scsi_target.name);
+ if (data->scsi_target.flags & VIR_NODE_DEV_CAP_FLAG_FC_RPORT) {
+ virBufferAddLit(&buf, "<capability type='fc_remote_port'>\n");
+ virBufferAdjustIndent(&buf, 2);
+ virBufferAsprintf(&buf, "<rport>%s</rport>\n",
+ data->scsi_target.rport);
+ virBufferAsprintf(&buf, "<wwpn>%s</wwpn>\n",
+ data->scsi_target.wwpn);
+ virBufferAdjustIndent(&buf, -2);
+ virBufferAddLit(&buf, "</capability>\n");
+ }
break;
case VIR_NODE_DEV_CAP_SCSI:
virNodeDeviceCapSCSIDefFormat(&buf, data);
xmlNodePtr node,
virNodeDevCapSCSITargetPtr scsi_target)
{
- xmlNodePtr orignode;
- int ret = -1;
+ xmlNodePtr orignode, *nodes = NULL;
+ int ret = -1, n = 0;
+ size_t i;
+ char *type = NULL;
orignode = ctxt->node;
ctxt->node = node;
goto out;
}
+ if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0)
+ goto out;
+
+ for (i = 0; i < n; ++i) {
+ type = virXMLPropString(nodes[i], "type");
+
+ if (!type) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing type for SCSI target capability for '%s'"),
+ def->name);
+ goto out;
+ }
+
+ if (STREQ(type, "fc_remote_port")) {
+ xmlNodePtr orignode2;
+
+ scsi_target->flags |= VIR_NODE_DEV_CAP_FLAG_FC_RPORT;
+
+ orignode2 = ctxt->node;
+ ctxt->node = nodes[i];
+
+ if (virNodeDevCapsDefParseString("string(./rport[1])",
+ ctxt,
+ &scsi_target->rport) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing rport name for '%s'"), def->name);
+ goto out;
+ }
+
+ if (virNodeDevCapsDefParseString("string(./wwpn[1])",
+ ctxt, &scsi_target->wwpn) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing wwpn identifier for '%s'"),
+ def->name);
+ goto out;
+ }
+
+ ctxt->node = orignode2;
+ } else {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown SCSI target capability type '%s' for '%s'"),
+ type, def->name);
+ goto out;
+ }
+
+ VIR_FREE(type);
+ }
+
ret = 0;
out:
ctxt->node = orignode;
+ VIR_FREE(type);
+ VIR_FREE(nodes);
return ret;
}
break;
case VIR_NODE_DEV_CAP_SCSI_TARGET:
VIR_FREE(data->scsi_target.name);
+ VIR_FREE(data->scsi_target.rport);
+ VIR_FREE(data->scsi_target.wwpn);
break;
case VIR_NODE_DEV_CAP_SCSI:
VIR_FREE(data->scsi.type);
VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS = (1 << 1),
} virNodeDevSCSIHostCapFlags;
+typedef enum {
+ VIR_NODE_DEV_CAP_FLAG_FC_RPORT = (1 << 0),
+} virNodeDevSCSITargetCapsFlags;
+
typedef enum {
VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION = (1 << 0),
VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION = (1 << 1),
typedef virNodeDevCapSCSITarget *virNodeDevCapSCSITargetPtr;
struct _virNodeDevCapSCSITarget {
char *name;
+ unsigned int flags; /* enum virNodeDevSCSITargetCapsFlags */
+ char *rport;
+ char *wwpn;
};
typedef struct _virNodeDevCapSCSI virNodeDevCapSCSI;
case VIR_NODE_DEV_CAP_SCSI_HOST:
nodeDeviceSysfsGetSCSIHostCaps(&cap->data.scsi_host);
break;
+ case VIR_NODE_DEV_CAP_SCSI_TARGET:
+ nodeDeviceSysfsGetSCSITargetCaps(dev->def->sysfs_path,
+ &cap->data.scsi_target);
+ break;
case VIR_NODE_DEV_CAP_NET:
if (virNetDevGetLinkInfo(cap->data.net.ifname, &cap->data.net.lnk) < 0)
return -1;
case VIR_NODE_DEV_CAP_SYSTEM:
case VIR_NODE_DEV_CAP_USB_DEV:
case VIR_NODE_DEV_CAP_USB_INTERFACE:
- case VIR_NODE_DEV_CAP_SCSI_TARGET:
case VIR_NODE_DEV_CAP_SCSI:
case VIR_NODE_DEV_CAP_STORAGE:
case VIR_NODE_DEV_CAP_FC_HOST:
#include <sys/stat.h>
#include <stdlib.h>
+#include "dirname.h"
#include "node_device_driver.h"
#include "node_device_hal.h"
#include "node_device_linux_sysfs.h"
#include "virerror.h"
#include "viralloc.h"
+#include "virfcp.h"
#include "virlog.h"
#include "virfile.h"
#include "virscsihost.h"
}
+int
+nodeDeviceSysfsGetSCSITargetCaps(const char *sysfsPath,
+ virNodeDevCapSCSITargetPtr scsi_target)
+{
+ int ret = -1;
+ char *dir = NULL, *rport = NULL;
+
+ VIR_DEBUG("Checking if '%s' is an FC remote port", scsi_target->name);
+
+ /* /sys/devices/[...]/host0/rport-0:0-0/target0:0:0 -> rport-0:0-0 */
+ if (!(dir = mdir_name(sysfsPath)))
+ return -1;
+
+ if (VIR_STRDUP(rport, last_component(dir)) < 0)
+ goto cleanup;
+
+ if (!virFCIsCapableRport(rport))
+ goto cleanup;
+
+ VIR_FREE(scsi_target->rport);
+ VIR_STEAL_PTR(scsi_target->rport, rport);
+
+ if (virFCReadRportValue(scsi_target->rport, "port_name",
+ &scsi_target->wwpn) < 0) {
+ VIR_WARN("Failed to read port_name for '%s'", scsi_target->rport);
+ goto cleanup;
+ }
+
+ scsi_target->flags |= VIR_NODE_DEV_CAP_FLAG_FC_RPORT;
+ ret = 0;
+
+ cleanup:
+ if (ret < 0) {
+ VIR_FREE(scsi_target->rport);
+ VIR_FREE(scsi_target->wwpn);
+ scsi_target->flags &= ~VIR_NODE_DEV_CAP_FLAG_FC_RPORT;
+ }
+ VIR_FREE(rport);
+ VIR_FREE(dir);
+
+ return ret;
+}
+
+
static int
nodeDeviceSysfsGetPCISRIOVCaps(const char *sysfsPath,
virNodeDevCapPCIDevPtr pci_dev)
return -1;
}
+int nodeDeviceSysfsGetSCSITargetCaps(const char *sysfsPath ATTRIBUTE_UNUSED,
+ virNodeDevCapSCSITargetPtr scsi_target ATTRIBUTE_UNUSED)
+{
+ return -1;
+}
+
int
nodeDeviceSysfsGetPCIRelatedDevCaps(const char *sysfsPath ATTRIBUTE_UNUSED,
virNodeDevCapPCIDevPtr pci_dev ATTRIBUTE_UNUSED)
# include "node_device_conf.h"
int nodeDeviceSysfsGetSCSIHostCaps(virNodeDevCapSCSIHostPtr scsi_host);
+int nodeDeviceSysfsGetSCSITargetCaps(const char *sysfsPath,
+ virNodeDevCapSCSITargetPtr scsi_target);
int nodeDeviceSysfsGetPCIRelatedDevCaps(const char *sysfsPath,
virNodeDevCapPCIDevPtr pci_dev);
}
-static int udevProcessSCSITarget(struct udev_device *device ATTRIBUTE_UNUSED,
+static int udevProcessSCSITarget(struct udev_device *device,
virNodeDeviceDefPtr def)
{
const char *sysname = NULL;
if (VIR_STRDUP(scsi_target->name, sysname) < 0)
return -1;
+ nodeDeviceSysfsGetSCSITargetCaps(def->sysfs_path, &def->caps->data.scsi_target);
+
if (udevGenerateDeviceName(device, def, NULL) != 0)
return -1;
--- /dev/null
+<device>
+ <name>scsi_target1_0_0</name>
+ <path>/sys/devices/css0/0.0.0000/0.0.0000/host1/rport-1:0-0/target1:0:0</path>
+ <parent>scsi_host0</parent>
+ <capability type='scsi_target'>
+ <target>target1:0:0</target>
+ <capability type='fc_remote_port'>
+ <rport>rport-1:0-0</rport>
+ <wwpn>0x9d73bc45f0e21a86</wwpn>
+ </capability>
+ </capability>
+</device>
DO_TEST("pci_0000_00_02_0_header_type");
DO_TEST("pci_0000_00_1c_0_header_type");
DO_TEST("scsi_target0_0_0");
+ DO_TEST("scsi_target1_0_0");
DO_TEST("pci_0000_02_10_7_sriov");
DO_TEST("pci_0000_02_10_7_sriov_vfs");
DO_TEST("pci_0000_02_10_7_sriov_zero_vfs_max_count");