[master 2/2] Add EDD 4 code and some logging cleanups.

vathpela installerbot-noreply at redhat.com
Thu Oct 1 15:25:12 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 | 242 ++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 185 insertions(+), 57 deletions(-)

diff --git a/blivet/devicelibs/edd.py b/blivet/devicelibs/edd.py
index f17eb2c..4eefe4c 100644
--- a/blivet/devicelibs/edd.py
+++ b/blivet/devicelibs/edd.py
@@ -32,39 +32,118 @@
 
 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
+        # the path we're probing
+        self.sysfspath = sysfspath
 
-        self.ata_device = None
-        self.channel = None
+        # data from edd/int13_devXX/version
+        self.version = self._read_file(os.path.join(sysfspath, "version"))
+
+        # data from edd/int13_devXX/mbr_signature
         self.mbr_signature = None
+
+        # data from edd/int13_devXX/sectors
+        self.sectors = None
+
+        # data from edd/int13_devXX/host_bus
+        self.host_bus = None
         self.pci_dev = None
+        self.channel = None
+
+        # data from edd/int13_devXX/interface in (nearly) all cases
+        self.type = None
+
+        # data from edd/int13_devXX/interface when type is ATAPI
+        self.atapi_device = None
+        self.atapi_lun = None
+
+        # data from edd/int13_devXX/interface when type is ATA or SATA
+        self.ata_device = None
+        self.ata_pmp = None
+
+        # data from edd/int13_devXX/interface when type is SCSI
         self.scsi_id = None
         self.scsi_lun = None
-        self.sectors = None
-        self.sysfslink = ""
-        self.sysfspath = sysfspath
-        self.version = self._read_file(os.path.join(sysfspath, "version"))
+
+        # data from edd/int13_devXX/interface when type is USB
+        self.usb_serial = None
+
+        # data from edd/int13_devXX/interface when type is 1394
+        self.ieee1394_eui64 = None
+
+        # data from edd/int13_devXX/interface when type is FIBRE
+        self.fibre_wwid = None
+        self.fibre_lun = None
+
+        # data from edd/int13_devXX/interface when type is I2O
+        self.i2o_identity = None
+
+        # data from edd/int13_devXX/interface when type is SAS
+        self.sas_address = None
+        self.sas_lun = None
+
+        # data we may get as the result of the search
+        self.sysfslink = None
 
         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_signature)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_signature)s, " \
+            "sectors: %(sectors)s"
+        if self.sysfslink != None:
+            s += "\n\tsysfs pci path: %(sysfslink)s"
+        if self.host_bus !=None or self.pci_dev != None or self.channel != None:
+            s += "\n\thost_bus: %(host_bus)s pci_dev: %(pci_dev)s "\
+                "channel: %(channel)s"
+        if self.atapi_device != None or self.atapi_lun != None:
+            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 self.scsi_id != None or self.scsi_lun != None:
+            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 self.fibre_wwid != None or self.fibre_lun != None:
+            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 self.sas_address != None or self.sas_lun != None:
+            s += "\n\tsas_address: %(sas_address)s sas_lun: %(sas_lun)s"
+
+        return s % self.__dict__
 
     def _read_file(self, filename):
         contents = None
@@ -77,10 +156,10 @@ def load(self):
         interface = self._read_file(os.path.join(self.sysfspath, "interface"))
         if interface:
             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)
+            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))
@@ -88,9 +167,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)
@@ -102,10 +214,11 @@ def load(self):
             self.sectors = int(sectors)
         hbus = self._read_file(os.path.join(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)
@@ -137,15 +250,24 @@ def devname_from_ata_pci_dev(self):
             # where literally the only pieces of data here are
             # "pci0000:00:1f.2", "ata1", and "sda".
             #
-            # EDD's "channel" may or may not mean anything at all on SATA
-            # (there's no spec...), and it's not clear if SATA Port
-            # Multipliers are represented in any way, or if they happen to
-            # be the channel (but in a malformed way).  I'm just assuming
-            # channel = 255 really means pmp_id == 65535, aka there isn't
-            # one.  If I'm wrong, then there's just no way to support SATA
-            # Port Multipliers, in which case the value will still say
-            # 255, and the device just won't be matched correctly ever.
-
+            # EDD 3's "channel" may or may not mean anything at all on SATA
+            # (there's no spec...), and SATA Port Multipliers aren't
+            # represented in any way.  In EDD 3, 0xff means
+            # "not in use", and there's no provision for a port multiplier.
+            # So basically "channel" means master/slave for ATA (non-SATA)
+            # devices.
+            #
+            # 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[5] != '0000:%s' % (self.edd.pci_dev,):
                 continue
             if not components[6].startswith('ata'):
@@ -162,28 +284,28 @@ def devname_from_ata_pci_dev(self):
                 continue
 
             fn = components[0:7] + ['link%d' % (ata_port_idx,),]
-            if self.edd.channel == 255:
-                # find all the things that are linkN/devN.X
-                # but filter out devN.X.Y
-                exp = fn + [r'dev%d\.\d+$' % (ata_port_idx,)]
-                pattern = fn + ['dev%d.*' % (ata_port_idx,)]
-            else:
-                # find all the things that are linkN/devN.X
-                # but filter out devN.X.Y
-                exp = fn + [r'dev%d\.(\d+)\.\d+$' % (ata_port_idx,)]
-                pattern = fn + ['dev%d.*.*' % (ata_port_idx,)]
-
-            exp = '^' + os.path.join(*exp)
-            exp = re.compile(exp)
-            pattern = os.path.join(*pattern)
-            for atapath in glob.iglob(pattern):
-                match = exp.match(atapath)
+            exp = [r'^']+fn+[r'dev%d\.(\d+)(\.(\d+)){0,1}$' % (ata_port_idx,)]
+            exp = os.path.join(*exp)
+            expmatcher = re.compile(exp)
+            pmp_glob = fn + ['dev%d.*.*' % (ata_port_idx,)]
+            pmp_glob = os.path.join(*pmp_glob)
+            dev_glob = fn + ['dev%d.*' % (ata_port_idx,)]
+            dev_glob = os.path.join(*dev_glob)
+            atapaths = tuple(glob.iglob(pmp_glob)) + tuple(glob.iglob(dev_glob))
+            for atapath in atapaths:
+                match = expmatcher.match(atapath)
                 if match is None:
                     continue
-                self.edd.sysfslink=link
-                if self.edd.channel == 255:
-                    return path.split('/')[-1]
-                elif self.edd.channel == int(match.group(1)):
+                # so at this point it's devW.X.Y or devW.Z as such:
+                # dev_set_name(dev, "dev%d.%d", ap->print_id,ata_dev->devno);
+                # dev_set_name(dev, "dev%d.%d.0", ap->print_id, link->pmp);
+                # we care about print_id and pmp for matching...
+                pmp = None
+                device = int(match.group(1))
+                if match.group(3) is not None:
+                    pmp = int(match.group(2))
+                if pmp == self.edd.ata_pmp and device == self.edd.ata_device:
+                    self.edd.sysfslink = link
                     return path.split('/')[-1]
         return None
 
@@ -202,6 +324,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):
@@ -214,17 +337,22 @@ 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)
         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":
+        elif self.edd.type in "SCSI":
             name = self.devname_from_scsi_pci_dev()
         return name
 


-- 
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/73cb55e8fd8cfe9448226adb50467f8d5f4e52b7


More information about the anaconda-patches mailing list