summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorAdam Smith <adamsmith@yzguy.io>2026-09-29 14:17:42 +0000
committerAdam Smith <adamsmith@yzguy.io>2026-09-29 15:24:34 +0000
commit90887491e9c0d122c8760195f37e01b1e7e8cdcc (patch)
treeaf92b58608c9899c95c5ad85226b05fb3f16717c /python
parenta272bffcf16c6c5ced83fb19a35aa3c78bbf7bff (diff)
downloadvyos-1x-90887491e9c0d122c8760195f37e01b1e7e8cdcc.tar.gz
vyos-1x-90887491e9c0d122c8760195f37e01b1e7e8cdcc.zip
wwan: T7487: only wait for a modem in start_modem_manager()
modem_index() unconditionally polled for up to modem_wait_timeout before returning None, so every caller that expects "no modem" as a routine outcome - interface teardown, admin-disconnect markers, disconnect/connect status checks, and vyos-netlinkd's reconcile loop checking every configured interface once per pass - blocked on it too. The reconcile loop in particular calls is_wwan_connected() synchronously, not per-interface, so one interface with no modem stalled the whole daemon's reconcile pass, including unrelated DHCP interfaces, every cycle. Add a wait parameter, default False (single check, returns immediately). Only start_modem_manager() passes wait=True, where waiting for a still-initializing modem is the actual point.
Diffstat (limited to 'python')
-rw-r--r--python/vyos/utils/wwan.py41
1 files changed, 26 insertions, 15 deletions
diff --git a/python/vyos/utils/wwan.py b/python/vyos/utils/wwan.py
index 6b07e90bb..6b708e39c 100644
--- a/python/vyos/utils/wwan.py
+++ b/python/vyos/utils/wwan.py
@@ -54,11 +54,14 @@ modem_wait_poll = 0.250
# survive a reboot, and a commit re-asserts the configured state too.
admin_disconnect_dir = '/run/vyos-wwan'
+
def _modem_owning_port(ifname: str):
- """ Return the ModemManager modem index whose own port list includes
- ifname, or None if no currently-known modem owns it. """
+ """Return the ModemManager modem index whose own port list includes
+ ifname, or None if no currently-known modem owns it."""
try:
- modem_list = loads(cmdl(['mmcli', '--list-modems', '--output-json'])).get('modem-list', [])
+ modem_list = loads(cmdl(['mmcli', '--list-modems', '--output-json'])).get(
+ 'modem-list', []
+ )
except OSError:
return None
@@ -74,8 +77,9 @@ def _modem_owning_port(ifname: str):
return None
-def modem_index(ifname: str):
- """ Return the ModemManager modem index backing an interface, or None if
+
+def modem_index(ifname: str, wait: bool = False):
+ """Return the ModemManager modem index backing an interface, or None if
no modem currently owns it.
The kernel-assigned WWAN interface number and ModemManager's own modem
@@ -85,20 +89,24 @@ def modem_index(ifname: str):
ModemManager currently knows about is listed, and whichever one
actually has ifname among its own ports is returned.
- A modem that has not shown up on the bus yet looks the same here as one
- that will never exist, so this waits up to modem_wait_timeout for it to
- appear before giving up - the same budget start_modem_manager already
- gives a modem to become dialable, since a modem slow enough to need
- that budget is also slow enough to not be listed yet when this is first
- called. """
+ "No modem owns this yet" is a routine result for most callers (deleted
+ interfaces, admin-disconnect markers, vyos-netlinkd's reconcile loop), so
+ by default this checks once and returns immediately. Only
+ start_modem_manager() passes wait=True, to give a modem that is still
+ enumerating up to modem_wait_timeout to show up."""
if not ifname.startswith('wwan'):
raise ValueError(f'Specified interface "{ifname}" is not a WWAN interface')
+ if not wait:
+ return _modem_owning_port(ifname)
+
index = None
+
def _resolve():
nonlocal index
index = _modem_owning_port(ifname)
return index is not None
+
wait_for(_resolve, interval=modem_wait_poll, timeout=modem_wait_timeout)
return index
@@ -128,7 +136,7 @@ def start_modem_manager(ifname: str) -> None:
if not is_systemd_service_active(service_name):
cmdl(['systemctl', 'start', service_name])
- index = modem_index(ifname)
+ index = modem_index(ifname, wait=True)
if index is None:
# No modem currently owns ifname at all - nothing more this can do.
# Matches the pre-existing tolerance for hardware that isn't detected
@@ -205,9 +213,12 @@ def modem_disconnect(ifname: str, quiet: bool = False) -> None:
modem = modem_index(ifname)
if modem is None:
return None
- call(f'mmcli --modem {modem} --simple-disconnect',
- stdout=DEVNULL if quiet else None,
- stderr=DEVNULL if quiet else None)
+ call(
+ f'mmcli --modem {modem} --simple-disconnect',
+ stdout=DEVNULL if quiet else None,
+ stderr=DEVNULL if quiet else None,
+ )
+
def modem_connect(ifname: str, options: str) -> bool:
""" Dial the modem backing ifname, returns True if the modem connected """