If the DeviceContext is cancelled, we remove it from the list of
tracked DeviceContexts in the MMPluginManager, so that new port
additions happening at the same time don't end up being added to
it.
This should allow an early cancellation of the DeviceContext without
asserting on g_assert (!device_context->extra_probing_time_id) as
there is no chance that a new port addition has reseted the extra
probing time timeout.
0x00007c1154f16abf (libc.so.6 - pthread_kill.c: 44) __pthread_kill_implementation
0x00007c1154ecbf0c (libc.so.6 - raise.c: 26) raise
0x00007c1154eb74aa (libc.so.6 - abort.c: 79) abort
0x00007c11556c08a9 (libglib-2.0.so.0 - gtestutils.c: 3450) g_assertion_message
0x00007c11556c091d (libglib-2.0.so.0 - gtestutils.c: 3476) g_assertion_message_expr
0x00005897d1ed6e7b (ModemManager - mm-plugin-manager.c: 805) device_context_unref
0x00005897d1ed6ab9 (ModemManager - mm-plugin-manager.c: 163) common_async_context_free
0x00005897d1ed5dd6 (ModemManager - mm-plugin-manager.c: 1136) port_context_run_ready
0x00007c1155550e86 (libgio-2.0.so.0 - gtask.c: 1309) g_task_return_now
0x00007c115554fe50 (libgio-2.0.so.0 - gtask.c: 1378) g_task_return
0x00007c115555047b (libgio-2.0.so.0 - gtask.c: 2022) g_task_return_new_error
0x00005897d1ed581a (ModemManager - mm-plugin-manager.c: 296) port_context_complete
0x00005897d1ed6259 (ModemManager - mm-plugin-manager.c) plugin_supports_port_ready
0x00007c1155550e86 (libgio-2.0.so.0 - gtask.c: 1309) g_task_return_now
0x00007c1155550eb9 (libgio-2.0.so.0 - gtask.c: 1323) complete_in_idle_cb
0x00007c115569c91b (libglib-2.0.so.0 - gmain.c: 3460) g_main_dispatch
0x00007c115569c91b (libglib-2.0.so.0 - gmain.c: 4200) g_main_context_dispatch
0x00007c115569cc37 (libglib-2.0.so.0 - gmain.c: 4276) g_main_context_iterate
0x00007c115569ceb5 (libglib-2.0.so.0 - gmain.c: 4479) g_main_loop_run
0x00005897d1e715bf (ModemManager - main.c: 236) main
0x00007c1154eb7705 (libc.so.6 - libc_start_call_main.h: 58) __libc_start_call_main
0x00007c1154eb77c1 (libc.so.6 - libc-start.c: 360) __libc_start_main_impl
0x00005897d1e70f10 (ModemManager + 0x000b3f10) _start
0x00007ffd5acc5057
Manually reproducing this issue was challenging, so adding here the
steps taken, for reference. This is with ModemManager running with
--no-udev (i.e. port additions reported via mmcli). The key to
reproduce the problem is the different timing between the port
additions and removals of the three ttyACM ports.
COUNT=1
while ((COUNT < 10000)); do
echo "$COUNT..."
COUNT=$((COUNT + 1))
(
sudo mmcli --report-kernel-event="name=ttyACM0,subsystem=tty,action=add"
TIMEOUT="0.0$(((RANDOM % 10) + 1))"
sleep $TIMEOUT
sudo mmcli --report-kernel-event="name=ttyACM0,subsystem=tty,action=remove"
) &
(
sudo mmcli --report-kernel-event="name=ttyACM1,subsystem=tty,action=add"
TIMEOUT="0.0$(((RANDOM % 10) + 1))"
sleep $TIMEOUT
sudo mmcli --report-kernel-event="name=ttyACM1,subsystem=tty,action=remove"
) &
(
sudo mmcli --report-kernel-event="name=ttyACM2,subsystem=tty,action=add"
TIMEOUT="0.0$(((RANDOM % 10) + 1))"
sleep $TIMEOUT
sudo mmcli --report-kernel-event="name=ttyACM2,subsystem=tty,action=remove"
) &
TIMEOUT="0.0$(((RANDOM % 10) + 1))"
sleep $TIMEOUT
COUNT=$((COUNT + 1))
done
There is no guarantee from the kernel that the ports of a given device
will all be notified via uevents on a timely manner. We assume it, but
we cannot be completely sure.
There are two main scenarios that we have been seen when the ports of a
device are not notified close enough to each other:
* A modem object is created, but not with the desired control protocol.
E.g. a QMI or MBIM modem would end up controlled via AT commands if
the cdc-wdm port was added too late.
* A modem object is not created at all, and the device object was
removed completely. E.g. when the net port addition in a QMI or MBIM
modem happens too late.
In order to avoid those cases, we will now fully reprobe all the ports
of the device from scratch whenever a new valid port addition happens
after the initial probing of the device has already been completed. If a
modem object is available at that time, we will remove it right away.
This will make device probing slower unconditionally, but it should
also ensure newer more complex devices that take longer to boot are
handled properly.
Instead of creating libmm-plugin* and libmm-shared* libraries that are
dlopen()-ed on runtime, allow incorporating all plugins into the
daemon binary itself.
This makes the startup of the daemon much faster and also avoids
issues with builds that require linker namespace isolation.
Fixes https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/674
<wrn> [plugin-manager] could not load shared '/usr/lib/ModemManager/libmm-shared-xmm.so': Missing major version info
Thread 1 "ModemManager" received signal SIGSEGV, Segmentation fault.
0x000055555562b79d in load_external_shared (path=<optimized out>, self=0x5555557b5880) at ../src/mm-plugin-manager.c:1885
1885 if (module && !(*shared_name))
(gdb) p module
$1 = (GModule *) 0x5555557b9670
(gdb) p shared_name
$2 = (const gchar **) 0x0
When using udev, we rely on the kernel to timely report port additions
in the same device.
When not using udev, e.g. when using hotplug scripts in openwrt, we
use mmcli --report-kernel-event operations to report the port
additions, which may end up requiring much more time to process,
especially during bootup as we would be reporting a lot of port
addition events one after the other.
Fixes https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/493
We keep the 'WHITELIST-ONLY' filter type name still as an option in
--filter-policy=[POLICY], but deprecated and with the better
'ALLOWLIST-ONLY' replacement suggested from now on.
Until now, the plugin whitelist rule in the filter would only handle
those plugins that require specific udev tags, and those that had
explicit full vid:pid pairs.
This update makes a new implicit automatic whitelist for all devices
that match any of the vids managed by the different plugins.
Looking at the current list of devices in the blacklist and greylist
maintained by ModemManager, there are no devices falling under the
list of supported plugin vids that would need to be blacklisted;
except for u-blox GPS modules:
huawei -> 0x12d1 -> None in blacklist/greylist
thuraya -> 0x1a26 -> None in blacklist/greylist
telit -> 0x1bc7 -> None in blacklist/greylist
dLink -> 0x2001 -> None in blacklist/greylist
wavecom -> 0x114f -> None in blacklist/greylist
x22x -> 0x1bbb,0x0b3c -> None in blacklist/greylist
anydata -> 0x16d5 -> None in blacklist/greylist
quectel -> 0x2c7c -> None in blacklist/greylist
haier -> 0x201e -> None in blacklist/greylist
novatel -> 0x1410 -> None in blacklist/greylist
dell -> 0x413c -> None in blacklist/greylist
option -> 0x0af0,0x1931 -> None in blacklist/greylist
nokia -> 0x0421 -> None in blacklist/greylist
cinterion -> 0x1e2d,0x0681 -> None in blacklist/greylist
simtech -> 0x1e0e -> None in blacklist/greylist
iridium -> 0x1edd -> None in blacklist/greylist
pantech -> 0x106c -> None in blacklist/greylist
longcheer -> 0x1c9e,0x1bbb -> None in blacklist/greylist
linktop -> 0x230d -> None in blacklist/greylist
sierra -> 0x1199 -> None in blacklist/greylist
ublox -> 0x1546 -------------> GPS chips blacklisted !!!!!
foxconn -> 0x0489 -> None in blacklist/greylist
broadmobi -> 0x2020 -> None in blacklist/greylist
fibocom -> 0x2cb7 -> None in blacklist/greylist
tplink -> 0x2357 -> None in blacklist/greylist
zte -> 0x19d2 -> None in blacklist/greylist
The rules used to blacklist the u-blox GPS chips have already been
moved to the u-blox plugin itself, and now they use the broader
ID_MM_DEVICE_IGNORE tag that applies in all filter modes.
Until now we had only a 2500ms timeout initialized since the first
port was exposed until we decided we were ready to consider all ports
notified by the kernel.
With this new logic, we add an additional condition: even if the
2500ms initial timeout has elapsed already, we leave an additional
1500ms since the last port addition for new ports to appear.
This new logic is useful when relying on the ReportKernelEvent() DBus
method, as it is the user the one responsible for reporting the kernel
events instead of udev. Now, the user is not forced to make sure all
ports are exposed in 2500ms; instead, we also allow ports to be
reported in more than 2500ms as long as the time between port
additions reported is less than 1500ms.
Note that this does not mean that the whole probing time will now
always be 4000ms. On well behaved systems (like when based on udev)
this new 'extra' probing timeout may expire long before the 'min'
probing timeout we already had as well.
E.g. in this setup, the reporting of the NET port was done 1100ms
later than the last ttyUSB3, and that was already too late as the
original 2500ms threshold had already expired.
[1573536994.593874] (tty/ttyUSB0): first port in device /sys/devices/platform/ehci-platform/usb1/1-1
[1573536994.596659] [plugin manager] task 1: port grabbed: ttyUSB0
[1573536995.093579] (tty/ttyUSB1): additional port in device /sys/devices/platform/ehci-platform/usb1/1-1
[1573536995.094172] [plugin manager] task 1: port grabbed: ttyUSB1
[1573536995.603206] (tty/ttyUSB2): additional port in device /sys/devices/platform/ehci-platform/usb1/1-1
[1573536995.603822] [plugin manager] task 1: port grabbed: ttyUSB2
[1573536996.111564] (tty/ttyUSB3): additional port in device /sys/devices/platform/ehci-platform/usb1/1-1
[1573536996.112257] [plugin manager] task 1: port grabbed: ttyUSB3
[1573536996.814816] [device /sys/devices/platform/ehci-platform/usb1/1-1] creating modem with plugin 'Quectel' and '4' ports
[1573536997.265820] (net/wwan0): additional port in device /sys/devices/platform/ehci-platform/usb1/1-1
[1573536997.296935] (usbmisc/cdc-wdm0): additional port in device /sys/devices/platform/ehci-platform/usb1/1-1
Several plugins define the specific device vid:pid pairs they
support. Let's use this information as a direct indication that
ModemManager can probe the devices.
Several plugins define specific udev tags that must be available in
the device in order for the plugins to use them. Let's use these tags
as a direct indication that ModemManager can probe the devices.
In particular, this change would make all Ericsson MBM modems probed
right away also in STRICT filter mode, without needing to check the
ttyACM interface type or the available net ports.
All the previous filter rules were applicable per-port independently.
But, we also want to apply rules on a port based on the existence of
other ports on the same device (e.g. allow TTY if the device also has
a NET port). In this case, we need to wait for all ports to appear and
then apply the additional rules.
We re-use the "min wait time" timeout in the plugin-manager for this
same purpose. This timeout is setup to wait for ports to appear before
starting the probing process (e.g. so that plugin filters like the
forbidden-drivers one work). The very same timeout can therefore be
used to check whether we start the probing or not based on additional
filter rules.
Cancelling the port_context->cancellable may end up finishing the
async task and completing the last reference of the port
context. Avoid that by making sure we hold a valid reference for as
long as we may need it.
==2277== Invalid read of size 4
==2277== at 0x14ACE2: port_context_cancel (mm-plugin-manager.c:547)
==2277== by 0x14C32E: device_context_port_released (mm-plugin-manager.c:1142)
==2277== by 0x83831C7: ffi_call_unix64 (in /usr/lib/libffi.so.6.0.4)
==2277== by 0x8382C29: ffi_call (in /usr/lib/libffi.so.6.0.4)
==2277== by 0x64506A8: g_cclosure_marshal_generic (in /usr/lib/libgobject-2.0.so.0.5200.3)
==2277== by 0x644FEAC: g_closure_invoke (in /usr/lib/libgobject-2.0.so.0.5200.3)
==2277== by 0x64624AD: ??? (in /usr/lib/libgobject-2.0.so.0.5200.3)
==2277== by 0x646AC84: g_signal_emit_valist (in /usr/lib/libgobject-2.0.so.0.5200.3)
==2277== by 0x646B69E: g_signal_emit (in /usr/lib/libgobject-2.0.so.0.5200.3)
==2277== by 0x1483E7: mm_device_release_port (mm-device.c:196)
==2277== by 0x145D5D: device_removed (mm-base-manager.c:217)
==2277== by 0x1464A9: handle_kernel_event (mm-base-manager.c:401)
==2277== Address 0xf677d18 is 88 bytes inside a block of size 96 free'd
==2277== at 0x4C2E14B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2277== by 0x14A1B7: port_context_unref (mm-plugin-manager.c:234)
==2277== by 0x14AB51: plugin_supports_port_ready (mm-plugin-manager.c:492)
==2277== by 0x612FD52: ??? (in /usr/lib/libgio-2.0.so.0.5200.3)
==2277== by 0x6130775: ??? (in /usr/lib/libgio-2.0.so.0.5200.3)
==2277== by 0x1AFDF8: port_probe_run_ready (mm-plugin.c:624)
==2277== by 0x612FD52: ??? (in /usr/lib/libgio-2.0.so.0.5200.3)
==2277== by 0x6130775: ??? (in /usr/lib/libgio-2.0.so.0.5200.3)
==2277== by 0x6131367: g_task_return_error_if_cancelled (in /usr/lib/libgio-2.0.so.0.5200.3)
==2277== by 0x1A9EDA: port_probe_task_return_error_if_cancelled (mm-port-probe.c:109)
==2277== by 0x1ABCD2: serial_probe_at_parse_response (mm-port-probe.c:900)
==2277== by 0x611E475: g_simple_async_result_complete (in /usr/lib/libgio-2.0.so.0.5200.3)
==2277== Block was alloc'd at
==2277== at 0x4C2CE5F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2277== by 0x66E3028: g_malloc (in /usr/lib/libglib-2.0.so.0.5200.3)
==2277== by 0x66FAB25: g_slice_alloc (in /usr/lib/libglib-2.0.so.0.5200.3)
==2277== by 0x66FAFB8: g_slice_alloc0 (in /usr/lib/libglib-2.0.so.0.5200.3)
==2277== by 0x14B18D: port_context_new (mm-plugin-manager.c:649)
==2277== by 0x14C4A7: device_context_port_grabbed (mm-plugin-manager.c:1182)
==2277== by 0x83831C7: ffi_call_unix64 (in /usr/lib/libffi.so.6.0.4)
==2277== by 0x8382C29: ffi_call (in /usr/lib/libffi.so.6.0.4)
==2277== by 0x64506A8: g_cclosure_marshal_generic (in /usr/lib/libgobject-2.0.so.0.5200.3)
==2277== by 0x644FEAC: g_closure_invoke (in /usr/lib/libgobject-2.0.so.0.5200.3)
==2277== by 0x64624AD: ??? (in /usr/lib/libgobject-2.0.so.0.5200.3)
==2277== by 0x646AC84: g_signal_emit_valist (in /usr/lib/libgobject-2.0.so.0.5200.3)
g_free and g_object_unref are in form of `void (*)(gpointer)`, which
matches the GDestroyNotify signature. An explicit GDestroyNotify cast on
g_free and g_object_unref is thus not needed.
Instead of relying constantly on GUdevDevice objects reported by GUdev, we now
use a new generic object (MMKernelDevice) for which we provide an initial GUdev
based backend.
All ports of the same modem reported by the kernel will all be associated with
a common 'uid' (unique id), which uniquely identifies the physical device. This
logic was already in place, what we do now is avoid calling it the 'sysfs
path' of the physical device, because we may not want to use that to identify
a device.
This logic now also enables the possibility of "naming" the modems in a unique
way by setting the "ID_MM_PHYSDEV_UID" property in the "usb_device" that owns
all the ports.
E.g. a custom device has 4 modems in 4 different USB ports. The device path of
each USB device will always be the same, so the naming rules could go like this:
$ vim /usr/lib/udev/rules.d/78-mm-naming.rules
ACTION!="add|change|move", GOTO="mm_naming_rules_end"
DEVPATH=="/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.5/4-1.5.1", ENV{ID_MM_PHYSDEV_UID}="USB-MODEM-1"
DEVPATH=="/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.5/4-1.5.2", ENV{ID_MM_PHYSDEV_UID}="USB-MODEM-2"
DEVPATH=="/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.5/4-1.5.3", ENV{ID_MM_PHYSDEV_UID}="USB-MODEM-3"
DEVPATH=="/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.5/4-1.5.4", ENV{ID_MM_PHYSDEV_UID}="USB-MODEM-4"
LABEL="mm_naming_rules_end"
Each of the modems found will have a unique UID retrieved from the previous list
of rules. Then, "mmcli" has also been updated to allow using the UID instead of
the modem DBus path or index, e.g.:
$ sudo mmcli -m USB-MODEM-1
/org/freedesktop/ModemManager1/Modem/0 (device id '988d83252c0598f670c2d69d5f41e077204a92fd')
-------------------------
Hardware | manufacturer: 'ZTE CORPORATION'
| model: 'MF637'
| revision: 'BD_W7P673A3F3V1.0.0B04'
| supported: 'gsm-umts'
| current: 'gsm-umts'
| equipment id: '356516027657837'
-------------------------
System | device: 'USB-MODEM-1'
| drivers: 'option'
| plugin: 'ZTE'
| primary port: 'ttyUSB5'
| ports: 'ttyUSB5 (at)'
...
$ sudo mmcli -m USB-MODEM-1 --enable
...
We really do need a wait time to make sure most ports are exposed by the
kernel, so that plugin filters based on "forbidden-drivers" work correctly. E.g.
the "gobi" plugin now flags as forbidden the "qmi_wwan" driver, which means that
modems exposing both TTYs and QMI/WWAN ports should never be handled by the Gobi
plugin.
If a task is marked as defer-until-suggested (e.g. a wwan port) and there was
already a 'best' plugin set in the parent device, use it to complete the
deferred task.
This may happen in e.g. MBIM devices if the WWAN port arrives later than the
already probed /dev/cdc-wdm character device.
Icera-based Nokia modems may reply correctly to the Icera probing only in some
AT ports, not in all. In order to handle this situation we override the final
plugin selected to be the Icera-based one if we find that the mismatched plugins
have the 'icera-allowed' and 'icera-forbidden' filters.
https://bugzilla.gnome.org/show_bug.cgi?id=703022
The specific case:
* Modem exposes cdc-wdm port, Generic plugin probes it successfully as QMI.
* Modem exposes new ports, including the wwan one. All ttys fail probing
because they're neither AT nor QCDM (CnS in this case).
* The wwan port ends up without a port being suggested and is not grabbed.
The root cause of this is that we do not propagate the suggested plugin to newly
added ports when it's the Generic one. If it wasn't the Generic one, the newly
added ports would start with the suggested one for probing.
Now, handle this by looking for the device-specified plugin when the port
probing ends without a specific port given. If there is such a device-specified
plugin accept the port, and otherwise, reject it.
For the case where we just get a device with all net ports (i.e. all deferred
until result suggested), just abort the probing if the minimum probing time is
consumed.