1
0
Fork 0

Merging upstream version 2.3~rc4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-16 12:55:12 +01:00
parent 0bb8b81ada
commit 3cb8664662
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
16 changed files with 708 additions and 276 deletions

View file

@ -11,6 +11,7 @@
import os
import time
import logging
from functools import partial
import pyudev
from gi.repository import GLib
from staslib import defs, iputil, trid
@ -154,7 +155,7 @@ class Udev:
return False
@staticmethod
def _cid_matches_tcp_tid_legacy(tid, cid): # pylint: disable=too-many-return-statements,too-many-branches
def _cid_matches_tcp_tid_legacy(tid, cid, ifaces): # pylint: disable=too-many-branches
'''On kernels older than 6.1, the src_addr parameter is not available
from the sysfs. Therefore, we need to infer a match based on other
parameters. And there are a few cases where we're simply not sure
@ -164,76 +165,66 @@ class Udev:
cid_host_iface = cid['host-iface']
cid_host_traddr = iputil.get_ipaddress_obj(cid['host-traddr'], ipv4_mapped_convert=True)
if not cid_host_iface: # cid.host_iface is undefined
if not cid_host_traddr: # cid.host_traddr is undefined
# When the existing cid.src_addr, cid.host_traddr, and cid.host_iface
# are all undefined (which can only happen on kernels prior to 6.1),
# we can't know for sure on which interface an existing connection
# was made. In this case, we can only declare a match if both
# tid.host_iface and tid.host_traddr are undefined as well.
logging.debug(
'Udev._cid_matches_tcp_tid_legacy() - cid=%s, tid=%s - Not enough info. Assume "match" but this could be wrong.',
cid,
tid,
)
return True
# Only check host_traddr if candidate cares about it
if tid.host_traddr:
tid_host_traddr = iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
# cid.host_traddr is defined. If tid.host_traddr is also
# defined, then it must match the existing cid.host_traddr.
if tid.host_traddr:
tid_host_traddr = iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
if cid_host_traddr:
if tid_host_traddr != cid_host_traddr:
return False
# If tid.host_iface is defined, then the interface where
# the connection is located must match. If tid.host_iface
# is not defined, then we don't really care on which
# interface the connection was made and we can skip this test.
if tid.host_iface:
# With the existing cid.host_traddr, we can find the
# interface of the exisiting connection.
connection_iface = iputil.get_interface(str(cid_host_traddr))
if tid.host_iface != connection_iface:
else:
# If c->cfg.host_traddr is unknown, then the controller (c)
# uses the interface's primary address as the source
# address. If c->cfg.host_iface is defined we can
# determine the primary address associated with that
# interface and compare that to the candidate->host_traddr.
if cid_host_iface:
if_addrs = ifaces.get(cid_host_iface, {4: [], 6: []})
source_addrs = if_addrs[tid_host_traddr.version]
if len(source_addrs): # Make sure it's not empty
primary_addr = iputil.get_ipaddress_obj(source_addrs[0], ipv4_mapped_convert=True)
if primary_addr != tid_host_traddr:
return False
else:
# If both c->cfg.host_traddr and c->cfg.host_iface are
# unknown, we don't have enough information to make a
# 100% positive match. Regardless, let's be optimistic
# and assume that we have a match.
logging.debug(
'Udev._cid_matches_tcp_tid_legacy() - [1] cid=%s, tid=%s - Not enough info. Assume "match" but this could be wrong.',
cid,
tid,
)
# Only check host_iface if candidate cares about it
if tid.host_iface:
if cid_host_iface:
if tid.host_iface != cid_host_iface:
return False
return True
else:
if cid_host_traddr:
connection_iface = iputil.get_interface(ifaces, cid_host_traddr)
if tid.host_iface != connection_iface:
return False
# cid.host_iface is defined
if not cid_host_traddr: # cid.host_traddr is undefined
if tid.host_iface and tid.host_iface != cid_host_iface:
return False
if tid.host_traddr:
# It's impossible to tell the existing connection source
# address. So, we can't tell if it matches tid.host_traddr.
# However, if the existing host_iface has only one source
# address assigned to it, we can assume that the source
# address used for the existing connection is that address.
if_addrs = iputil.net_if_addrs().get(cid_host_iface, {4: [], 6: []})
tid_host_traddr = iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
source_addrs = if_addrs[tid_host_traddr.version]
if len(source_addrs) != 1:
return False
src_addr0 = iputil.get_ipaddress_obj(source_addrs[0], ipv4_mapped_convert=True)
if src_addr0 != tid_host_traddr:
return False
return True
# cid.host_traddr is defined
if tid.host_iface and tid.host_iface != cid_host_iface:
return False
if tid.host_traddr:
tid_host_traddr = iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
if tid_host_traddr != cid_host_traddr:
return False
else:
# If both c->cfg.host_traddr and c->cfg.host_iface are
# unknown, we don't have enough information to make a
# 100% positive match. Regardless, let's be optimistic
# and assume that we have a match.
logging.debug(
'Udev._cid_matches_tcp_tid_legacy() - [2] cid=%s, tid=%s - Not enough info. Assume "match" but this could be wrong.',
cid,
tid,
)
return True
@staticmethod
def _cid_matches_tid(tid, cid): # pylint: disable=too-many-return-statements,too-many-branches
def _cid_matches_tid(tid, cid, ifaces): # pylint: disable=too-many-return-statements,too-many-branches
'''Check if existing controller's cid matches candidate controller's tid.
@param cid: The Connection ID of an existing controller (from the sysfs).
@param tid: The Transport ID of a candidate controller.
@ -242,8 +233,8 @@ class Udev:
be re-used for the candidate controller (specified by tid).
We do not have a match if the candidate's tid.transport, tid.traddr,
tid.trsvcid, and tid.subsysnqn are not identical to those of the cid.
These 4 parameters are mandatory for a match.
tid.trsvcid, tid.subsysnqn, and tid.host_nqn are not identical to those
of the cid. These 5 parameters are mandatory for a match.
The tid.host_traddr and tid.host_iface depend on the transport type.
These parameters may not apply or have a different syntax/meaning
@ -274,8 +265,9 @@ class Udev:
tid_traddr = iputil.get_ipaddress_obj(tid.traddr, ipv4_mapped_convert=True)
cid_traddr = iputil.get_ipaddress_obj(cid['traddr'], ipv4_mapped_convert=True)
else:
cid_traddr = cid['traddr']
tid_traddr = tid.traddr
# For FC and loop we can do a case-insensitive comparison
tid_traddr = tid.traddr.lower()
cid_traddr = cid['traddr'].lower()
if cid_traddr != tid_traddr:
return False
@ -290,7 +282,7 @@ class Udev:
# For legacy kernels (i.e. older than 6.1), the existing cid.src_addr
# is always undefined. We need to use advanced logic to determine
# whether cid and tid match.
return Udev._cid_matches_tcp_tid_legacy(tid, cid)
return Udev._cid_matches_tcp_tid_legacy(tid, cid, ifaces)
# The existing controller's cid.src_addr is always defined for kernel
# 6.1 and later. We can use the existing controller's cid.src_addr to
@ -303,12 +295,12 @@ class Udev:
return False
# host-iface is an optional tcp-only parameter.
if tid.host_iface and tid.host_iface != iputil.get_interface(str(src_addr)):
if tid.host_iface and tid.host_iface != iputil.get_interface(ifaces, src_addr):
return False
elif tid.transport == 'fc':
# host-traddr is mandatory for FC.
if tid.host_traddr != cid['host-traddr']:
if tid.host_traddr.lower() != cid['host-traddr'].lower():
return False
elif tid.transport == 'rdma':
@ -326,17 +318,20 @@ class Udev:
Discovery Controller.
@return The device if a match is found, None otherwise.
'''
for device in self._context.list_devices(
devices = self._context.list_devices(
subsystem='nvme', NVME_TRADDR=tid.traddr, NVME_TRSVCID=tid.trsvcid, NVME_TRTYPE=tid.transport
):
if not self.is_dc_device(device):
continue
)
if devices:
ifaces = iputil.net_if_addrs()
for device in devices:
if not self.is_dc_device(device):
continue
cid = self.get_cid(device)
if not self._cid_matches_tid(tid, cid):
continue
cid = self.get_cid(device)
if not self._cid_matches_tid(tid, cid, ifaces):
continue
return device
return device
return None
@ -345,17 +340,20 @@ class Udev:
I/O Controller.
@return The device if a match is found, None otherwise.
'''
for device in self._context.list_devices(
devices = self._context.list_devices(
subsystem='nvme', NVME_TRADDR=tid.traddr, NVME_TRSVCID=tid.trsvcid, NVME_TRTYPE=tid.transport
):
if not self.is_ioc_device(device):
continue
)
if devices:
ifaces = iputil.net_if_addrs()
for device in devices:
if not self.is_ioc_device(device):
continue
cid = self.get_cid(device)
if not self._cid_matches_tid(tid, cid):
continue
cid = self.get_cid(device)
if not self._cid_matches_tid(tid, cid, ifaces):
continue
return device
return device
return None
@ -364,68 +362,63 @@ class Udev:
@return A list of pyudev.device._device.Device objects
'''
tids = []
for device in self._context.list_devices(subsystem='nvme'):
if device.properties.get('NVME_TRTYPE', '') not in transports:
continue
devices = self._context.list_devices(subsystem='nvme')
if devices:
ifaces = iputil.net_if_addrs()
for device in devices:
if device.properties.get('NVME_TRTYPE', '') not in transports:
continue
if not self.is_ioc_device(device):
continue
if not self.is_ioc_device(device):
continue
tids.append(self.get_tid(device))
tids.append(self.get_tid(device, ifaces))
return tids
def _process_udev_event(self, event_source, condition): # pylint: disable=unused-argument
if condition == GLib.IO_IN:
event_count = 0
while True:
try:
device = self._monitor.poll(timeout=0)
except EnvironmentError as ex:
device = None
# This event seems to happen in bursts. So, let's suppress
# logging for 2 seconds to avoid filling the syslog.
self._log_event_count += 1
now = time.time()
if now > self._log_event_soak_time:
logging.debug('Udev._process_udev_event() - %s [%s]', ex, self._log_event_count)
self._log_event_soak_time = now + 2
self._log_event_count = 0
try:
self.__handle_events()
if device is None:
break
event_count += 1
self._device_event(device, event_count)
except EnvironmentError as ex:
# Exceptions seem to happen in bursts. So, let's suppress
# logging for 2 seconds to avoid filling the syslog.
self._log_event_count += 1
now = time.time()
if now > self._log_event_soak_time:
logging.debug('Udev._process_udev_event() - %s [%s]', ex, self._log_event_count)
self._log_event_soak_time = now + 2
self._log_event_count = 0
return GLib.SOURCE_CONTINUE
@staticmethod
def __cback_names(action_cbacks, device_cback):
names = []
for cback in action_cbacks:
names.append(cback.__name__ + '()')
if device_cback:
names.append(device_cback.__name__ + '()')
return names
def __handle_events(self):
event_count = 0
read_device = partial(self._monitor.poll, timeout=0)
for device in iter(read_device, None):
if device is None: # This should never happen,...
break # ...but better safe than sorry.
def _device_event(self, device, event_count):
action_cbacks = self._action_event_registry.get(device.action, set())
device_cback = self._device_event_registry.get(device.sys_name, None)
event_count += 1
logging.debug(
'Udev._device_event() - %-8s %-6s %-8s %s',
f'{device.sys_name}:',
device.action,
f'{event_count:2}:{device.sequence_number}',
self.__cback_names(action_cbacks, device_cback),
)
action_cbacks = self._action_event_registry.get(device.action, None)
device_cback = self._device_event_registry.get(device.sys_name, None)
if action_cbacks or device_cback:
logging.debug(
'Udev.__handle_events() - %-7s %-6s %2s:%s',
device.sys_name,
device.action,
event_count,
device.sequence_number,
)
for action_cback in action_cbacks:
GLib.idle_add(action_cback, device)
if action_cbacks:
for action_cback in action_cbacks:
GLib.idle_add(action_cback, device)
if device_cback is not None:
GLib.idle_add(device_cback, device)
if device_cback is not None:
GLib.idle_add(device_cback, device)
@staticmethod
def _get_property(device, prop, default=''):
@ -470,7 +463,7 @@ class Udev:
return attr_str[start:end]
@staticmethod
def get_tid(device):
def get_tid(device, ifaces):
'''@brief return the Transport ID associated with a udev device'''
cid = Udev.get_cid(device)
if cid['transport'] == 'tcp':
@ -479,7 +472,7 @@ class Udev:
# We'll try to find the interface from the source address on
# the connection. Only available if kernel exposes the source
# address (src_addr) in the "address" attribute.
cid['host-iface'] = iputil.get_interface(src_addr)
cid['host-iface'] = iputil.get_interface(ifaces, iputil.get_ipaddress_obj(src_addr))
return trid.TID(cid)