Skip to content

Resume() holds stale USB handle after S3 suspend — intermittent, existing retry doesn't reopen the device #281

Description

@Tora-Bora

Environment

  • Hardware: Lenovo ThinkPad X1 Yoga Gen 3
  • Sensor: 06cb:009a Synaptics, Inc. Metallica MIS Touch Fingerprint Reader
  • Distro: Ubuntu 24.04, kernel 6.14.0-36-generic
  • python3-validity 0.15~ppa2, open-fprintd 0.7~ppa2 (from PPA)

Summary

After deep (S3) suspend, Resume() frequently fails with
usb.core.USBError: [Errno 19] No such device — roughly half the
time on this machine, depending on how long the sensor was suspended
for. When the kernel re-enumerates the sensor as a new USB device
number, the service still holds the file descriptor from the
pre-suspend device; neither the primary open_common() call nor
the existing except-clause retry reopens the USB device.

When this happens, recovery requires systemctl restart python3-validity
(~1 second), which is long enough for GDM/gnome-shell to poll fprintd,
get an error, and fall back to password prompt — so the sensor never
lights up on lid-open. The rest of the time (short suspends where
USB is not re-enumerated) Resume() works fine.

The current Resume() already tries twice

defResume(self):
logging.debug('In Resume')
tls.reset()
try:
init.open_common()
except:
init.open_common() # same stale usb.dev — retry is a no-op

Both invocations issue commands through the same usb.dev object.
open_common() runs init_flash()usb.send_init() → writes over
self.dev — and there is no path in the resume flow that calls
usb.open() again. usb.open() is only invoked at process startup
via init.open().

Timeline of a failed resume (representative)

11:06:08 kernel: PM: suspend exit
11:06:08 open-fprintd: (from resume.py) Manager.Resume
11:06:08 python3-validity: In Resume <- old fd
11:06:09 gdm-fingerprint: gkr-pam: no password is available for user
(GDM has already polled fprintd, got an error, gave up)
11:06:09 dbus-service: USBError [Errno 19] x2 (initial + internal retry)
11:06:09 systemd: Stopped / Started python3-validity (external restart)
11:06:10 python3-validity: Manager is back online, registering
(too late — GDM has moved on)

Suggested fix

Reopen the USB device in the except-clause of Resume():

defResume(self):
logging.debug('In Resume')
tls.reset()
try:
init.open_common()
exceptException:
# USB device likely re-enumerated across S3; fd is stale.fromvaliditysensor.usbimportusbusb.close()
usb.open()
init.open_common()

This eliminates the need for the external process restart and closes
the race window with the desktop's fingerprint poll.

s2idle resume is unaffected because the USB device is not
re-enumerated — the sensor keeps the same device number, and the
existing retry succeeds. It's specifically the S3 case that breaks.

Related open-fprintd change

Locally I patched open-fprintd's resume.py to (a) cancel any
pending VerifyStop/Release before calling Resume(), and
(b) fall back to systemctl restart python3-validity when
Resume() throws. Happy to open a separate PR to
uunicorn/open-fprintd if that would be useful — but the
process-restart workaround only matters until Resume() handles
stale handles itself.

Reproducibility

Intermittent — approximately 50/50 in a recent 10-resume sample on
this hardware. Longer suspends (multiple hours) fail more often;
short suspends often succeed. The failure mode itself is consistent
whenever it triggers: same USBError from the same code path.

Related issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions