[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