[master 2/6] Add EDD 4 code and some logging cleanups.
vathpela
installerbot-noreply at redhat.com
Wed Oct 7 20:44:28 UTC 2015
From: Peter Jones <pjones at redhat.com>
This adds support for EDD4 types and fields (some of which the kernel
does not yet expose), as well as only logging relevant edd information.
Here's an example log from a system with good EDD data booted from USB,
installing to a locally connected SATA disk:
15:16:01,198 INFO blivet: edd: collected mbr signatures: {u'sda': '0x00022a9f', u'sdb': '0x946a5e4b'}
15:16:01,199 WARN blivet: edd: interface type USB is not implemented (/sys/firmware/edd/int13_dev80)
15:16:01,199 DEBUG blivet: edd: data extracted from 0x80:
path: /sys/firmware/edd/int13_dev80, version: 0x21
type: USB, mbr_signature: 0x946a5e4b, sectors: 15667200
host_bus: PCI pci_dev: ff:ff.255 channel: 255
usb_serial: 808463921
15:16:01,199 INFO blivet: edd: matched 0x80 to sdb using MBR sig
15:16:01,202 DEBUG blivet: edd: data extracted from 0x81:
path: /sys/firmware/edd/int13_dev81, version: 0x30
type: SATA, mbr_signature: 0x00022a9f, sectors: 312581808
sysfs pci path: ../devices/pci0000:00/0000:00:1f.2/ata1/host1/target1:0:0/1:0:0:0/block/sda
host_bus: PCI pci_dev: 00:1f.2 channel: 255
ata_device: 0
15:16:01,202 INFO blivet: edd: matched 0x81 to sda using pci_dev
Isn't that nice? It is nice.
Signed-off-by: Peter Jones <pjones at redhat.com>
---
blivet/devicelibs/edd.py | 259 +++++++++++++++++++++++++++++++++++++++++------
1 file changed, 229 insertions(+), 30 deletions(-)
diff --git a/blivet/devicelibs/edd.py b/blivet/devicelibs/edd.py
index 8a16eb5..60d34ea 100644
--- a/blivet/devicelibs/edd.py
+++ b/blivet/devicelibs/edd.py
@@ -34,50 +34,195 @@
log = logging.getLogger("blivet")
-re_host_bus = re.compile(r'^PCI\S*\s*(\S*)\s*channel: (\S*)\s*$')
-re_interface_scsi = re.compile(r'^SCSI\s*id: (\S*)\s*lun: (\S*)\s*$')
+re_host_bus_pci = re.compile(r'^(PCIX|PCI|XPRS|HTPT)\s*(\S*)\s*channel: (\S*)\s*$')
+re_interface_atapi = re.compile(r'^ATAPI\s*device: (\S*)\s*lun: (\S*)\s*$')
re_interface_ata = re.compile(r'^ATA\s*device: (\S*)\s*$')
-re_interface_sata = re.compile(r'^SATA\s*device: (\S*)\s*$')
+re_interface_scsi = re.compile(r'^SCSI\s*id: (\S*)\s*lun: (\S*)\s*$')
+re_interface_usb = re.compile(r'^USB\s*serial_number: (\S*)\s*$')
+re_interface_1394 = re.compile(r'^1394\s*eui: (\S*)\s*$')
+re_interface_fibre = re.compile(r'^FIBRE\s*wwid: (\S*)\s*lun: (\S*)\s*$')
+re_interface_i2o = re.compile(r'^I2O\s*identity_tag: (\S*)\s*$')
+# pretty sure the RAID definition using "identity_tag" is basically a kernel
+# bug, but it's part of the ABI now, so it sticks. The format of the
+# scnprintf() is at least correct.
+re_interface_raid = re.compile(r'^RAID\s*identity_tag: (\S*)\s*$')
+re_interface_edd3_sata = re.compile(r'^SATA\s*device: (\S*)\s*$')
+# EDD 4 features from 2010 and later. Awesomely, the "version" output from
+# int 13 AH=41h says: AH Version of extensions. Shall be set to 30h,
+# so there's no way to distinguish these from EDD 3, even thuogh SATA does
+# differ. In theory, if we're on <4.0, pmp should always be all 0's.
+re_interface_edd4_sata = re.compile(r'^SATA\s*device: (\S*)\s*pmp: (\S*)\s*$')
+re_interface_sas = re.compile(r'^SAS\s*sas_address: (\S*)\s*lun: \(\S*\)\s*$')
+# to make life difficult, when it finds an unknown interface type string,
+# the kernel prints the values without the string. But it does print the
+# anchoring tab that would go between them...
+re_interface_unknown = re.compile(r'^(\S*)\s*unknown: (\S*) (\S*)\s*$')
class EddEntry(object):
""" This object merely collects what the /sys/firmware/edd/* entries can
provide.
"""
def __init__(self, sysfspath):
- self.type = None
+ # some misc data from various files...
+ self.sysfspath = sysfspath
+ """ sysfspath is the path we're probing
+ """
+
+ self.sysfslink = None
+ """ The path /sys/block/BLAH is a symlink link to once we've resolved
+ that this is a particular device. Used for logging later.
+ """
+
+ self.version = util.get_sysfs_attr(sysfspath, "version")
+ """ The edd version this entry claims conformance with, from
+ /sys/firmware/edd/int13_devXX/version """
- self.ata_device = None
- self.channel = None
self.mbr_sig = None
+ """ The MBR signature data from edd/int13_devXX/mbr_signature """
+
+ self.sectors = None
+ """ The number of sectors on the device from edd/int13_devXX/sectors """
+
+ # Now the data from edd/int13_devXX/host_bus
+ self.host_bus = None
+ """ The ID string for the host bus type, from
+ edd/int13_devXX/host_bus.
+ """
self.pci_dev = None
+ """ The host bus bus:device.function, from edd/int13_devXX/host_bus.
+ """
+ self.channel = None
+ """ The host bus device's channel number edd/int13_devXX/host_bus.
+ The spec says:
+ Channel number. If more than one interface of the same type is
+ accessed through a single Bus, Slot, Function, then the channel
+ number shall identify each interface. If there is only one
+ interface, the content of this field shall be cleared to zero. If
+ there are two interfaces, such as an ATA Primary and Secondary
+ interface, the primary interface shall be zero, and the secondary
+ interface shall be one.
+
+ Values 00h through FEh shall represent a valid Channel Number.
+
+ Value FFh shall indicate that this field is not used.
+
+ If the device is connected to a SATA controller functioning in
+ non-PATA emulation mode, this byte shall be FFh.
+ """
+
+ # And now the various data from different formats of
+ # edd/int13_devXX/interface .
+ self.interface = None
+ """ interface is the actual contents of the interface file,
+ preserved for logging later.
+ """
+
+ self.type = None
+ """ The device type from edd/int13_devXX/interface.
+ """
+
+ self.atapi_device = None
+ """ The device number of the ATAPI device from
+ edd/int13_devXX/interface when self.type is ATAPI.
+ """
+ self.atapi_lun = None
+ """ The LUN of the ATAPI device from edd/int13_devXX/interface when
+ self.type is ATAPI.
+ """
+
+ self.ata_device = None
+ """ The device number from edd/int13_devXX/interface when self.type
+ is ATA or SATA (because Linux treats these the same.)
+ """
+ self.ata_pmp = None
+ """ The ATA port multiplier ID from edd/int13_devXX/interface when
+ self.type is SATA.
+ """
+
self.scsi_id = None
+ """ The SCSI device ID from edd/int13_devXX/interface when
+ self.type is SCSI
+ """
self.scsi_lun = None
- self.sectors = None
- self.sysfslink = ""
- self.sysfspath = sysfspath
- self.version = util.get_sysfs_attr(sysfspath, "version")
+ """ The SCSI device LUN from edd/int13_devXX/interface when
+ self.type is SCSI
+ """
+
+ self.usb_serial = None
+ """ The USB storage device's serial number from
+ edd/int13_devXX/interface when self.type is USB.
+ """
+
+ self.ieee1394_eui64 = None
+ """ The Firewire/IEEE-1394 EUI-64 ID from edd/int13_devXX/interface
+ when self.type is 1394.
+ """
+
+ self.fibre_wwid = None
+ """ The FibreChannel WWID from edd/int13_devXX/interface when
+ self.type is FIBRE.
+ """
+ self.fibre_lun = None
+ """ The FibreChannel LUN from edd/int13_devXX/interface when
+ self.type is FIBRE.
+ """
+
+ self.i2o_identity = None
+ """ The I2O Identity from edd/int13_devXX/interface when self.type
+ is I2O.
+ """
+
+ self.sas_address = None
+ """ The SAS Address from edd/int13_devXX/interface when self.type
+ is SAS.
+ """
+ self.sas_lun = None
+ """ The SAS LUN from edd/int13_devXX/interface when self.type is SAS.
+ """
self.load()
def __str__(self):
- return \
- "\tpath: %(sysfspath)s version: %(version)s\n" \
- "\tsysfs pci path: %(sysfslink)s\n" \
- "\ttype: %(type)s, ata_device: %(ata_device)s\n" \
- "\tchannel: %(channel)s, mbr_signature: %(mbr_sig)s\n" \
- "\tpci_dev: %(pci_dev)s, scsi_id: %(scsi_id)s," \
- " scsi_lun: %(scsi_lun)s, sectors: %(sectors)s" % self.__dict__
+ s = "\tpath: %(sysfspath)s, version: %(version)s\n" \
+ "\ttype: %(type)s, mbr_signature: %(mbr_sig)s, " \
+ "sectors: %(sectors)s"
+ if self.sysfslink != None:
+ s += "\n\tsysfs pci path: %(sysfslink)s"
+ if any([self.host_bus, self.pci_dev, self.channel]):
+ s += "\n\thost_bus: %(host_bus)s pci_dev: %(pci_dev)s "\
+ "channel: %(channel)s"
+ if any([self.atapi_device, self.atapi_lun]):
+ s += "\n\tatapi_device: %(atapi_device)s, atapi_lun: %(atapi_lun)s"
+ if self.ata_device != None:
+ s += "\n\tata_device: %(ata_device)s"
+ if self.ata_pmp != None:
+ s += ", ata_pmp: %(ata_pmp)s"
+ if any([self.scsi_id, self.scsi_lun]):
+ s += "\n\tscsi_id: %(scsi_id)s, scsi_lun: %(scsi_lun)s"
+ if self.usb_serial != None:
+ s += "\n\tusb_serial: %(usb_serial)s"
+ if self.ieee1394_eui64 != None:
+ s += "\n\t1394_eui: %(ieee1394_eui64)"
+ if any([self.fibre_wwid,self.fibre_lun]):
+ s += "\n\tfibre wwid: %(fibre_wwid)s, lun: %s(fibre_lun)s"
+ if self.i2o_identity != None:
+ s += "\n\ti2o_identity: %(i2o_identity)s"
+ if any([self.sas_address, self.sas_lun]):
+ s += "\n\tsas_address: %(sas_address)s sas_lun: %(sas_lun)s"
+
+ return s % self.__dict__
def load(self):
interface = util.get_sysfs_attr(self.sysfspath, "interface")
+ # save this so we can log it from the matcher.
+ self.interface = interface
if interface:
try:
self.type = interface.split()[0]
- unsupported = ("ATAPI", "USB", "1394", "FIBRE", "I2O", "RAID")
- if self.type in unsupported:
- log.warning("edd: interface type %s is not implemented (%s)",
- self.type, self.sysfspath)
- log.warning("edd: interface details: %s", interface)
+ if self.type == "ATAPI":
+ match = re_interface_atapi.match(interface)
+ self.atapi_device = int(match.group(1))
+ self.atapi_lun = int(match.group(2))
elif self.type == "ATA":
match = re_interface_ata.match(interface)
self.ata_device = int(match.group(1))
@@ -85,9 +230,42 @@ def load(self):
match = re_interface_scsi.match(interface)
self.scsi_id = int(match.group(1))
self.scsi_lun = int(match.group(2))
+ elif self.type == "USB":
+ match = re_interface_usb.match(interface)
+ self.usb_serial = int(match.group(1), base=16)
+ elif self.type == "1394":
+ match = re_interface_1394.match(interface)
+ self.ieee1394_eui64 = int(match.group(1), base=16)
+ elif self.type == "FIBRE":
+ match = re_interface_fibre.match(interface)
+ self.fibre_wwid = int(match.group(1), base=16)
+ self.fibre_lun = int(match.group(2), base=16)
+ elif self.type == "I2O":
+ match = re_interface_i2o.match(interface)
+ self.i2o_identity = int(match.group(1), base=16)
+ elif self.type == "RAID":
+ match = re_interface_raid.match(interface)
+ self.raid_array = int(match.group(1), base=16)
elif self.type == "SATA":
- match = re_interface_sata.match(interface)
- self.ata_device = int(match.group(1))
+ match = re_interface_edd4_sata.match(interface)
+ if match:
+ self.ata_device = int(match.group(1))
+ self.ata_pmp = int(match.group(2))
+ else:
+ match = re_interface_edd3_sata.match(interface)
+ self.ata_device = int(match.group(1))
+ elif self.type == "SAS":
+ sas_match = re_interface_sas.match(interface)
+ unknown_match = re_interface_unknown.match(interface)
+ if sas_match:
+ self.sas_address = int(sas_match.group(1), base=16)
+ self.sas_lun = int(sas_match.group(2), base=16)
+ elif unknown_match:
+ self.sas_address = int(unknown_match.group(1), base=16)
+ self.sas_lun = int(unknown_match.group(2), base=16)
+ else:
+ log.warning("edd: can not match interface for %s: %s",
+ self.sysfspath, interface)
else:
log.warning("edd: can not match interface for %s: %s",
self.sysfspath, interface)
@@ -104,10 +282,11 @@ def load(self):
self.sectors = int(sectors)
hbus = util.get_sysfs_attr(self.sysfspath, "host_bus")
if hbus:
- match = re_host_bus.match(hbus)
+ match = re_host_bus_pci.match(hbus)
if match:
- self.pci_dev = match.group(1)
- self.channel = int(match.group(2))
+ self.host_bus = match.group(1)
+ self.pci_dev = match.group(2)
+ self.channel = int(match.group(3))
else:
log.warning("edd: can not match host_bus for %s: %s",
self.sysfspath, hbus)
@@ -141,6 +320,17 @@ def devname_from_ata_pci_dev(self):
# for ATA (non-SATA) devices. Also in EDD 3, SATA port multipliers
# aren't represented in any way.
#
+ # In EDD 4, which unfortunately says to leave 0x30 as the version
+ # number, the port multiplier id is an additional field on the
+ # interface. So basically we should try to use the value the
+ # kernel gives us*, but we can't trust it. Thankfully there
+ # won't be a devX.Y.Z (i.e. a port multiplier device) in sysfs
+ # that collides with devX.Z (a non-port-multiplied device),
+ # so if we get a value from the kernel, we can try with and
+ # without it.
+ #
+ # * When the kernel finally learns of these facts...
+ #
if components[4] != '0000:%s' % (self.edd.pci_dev,):
continue
if not components[5].startswith('ata'):
@@ -182,8 +372,10 @@ def devname_from_ata_pci_dev(self):
self.edd.sysfslink = link
return path.split('/')[-1]
else:
- log.warning("edd: ATA Port multipliers are unsupported")
- continue;
+ pmp = int(match.group(1))
+ if self.edd.ata_pmp == pmp:
+ self.edd.sysfslink = link
+ return path.split('/')[-1]
return None
def devname_from_scsi_pci_dev(self):
@@ -201,6 +393,7 @@ def devname_from_scsi_pci_dev(self):
name = block_entries[0]
else:
log.warning("edd: directory does not exist: %s", path)
+ self.edd.sysfslink = path
return name
def devname_from_virt_pci_dev(self):
@@ -213,14 +406,20 @@ def devname_from_virt_pci_dev(self):
block_entries = os.listdir(matching_paths[0])
if len(block_entries) == 1:
name = block_entries[0]
+ self.edd.sysfslink = matching_paths[0]
return name
def devname_from_pci_dev(self):
name = self.devname_from_virt_pci_dev()
if not name is None:
return name
+
+ unsupported = ("ATAPI", "USB", "1394", "I2O", "RAID", "FIBRE", "SAS")
+ if self.edd.type in unsupported:
+ log.warning("edd: interface type %s is not implemented (%s)",
+ self.edd.type, self.edd.sysfspath)
+ log.warning("edd: interface details: %s", self.edd.interface)
if self.edd.type in ("ATA", "SATA") and \
- self.edd.channel is not None and \
self.edd.ata_device is not None:
name = self.devname_from_ata_pci_dev()
elif self.edd.type == "SCSI":
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/b2e155a2143cfbac5a8c9e17c009db2ec984facf
More information about the anaconda-patches
mailing list