Skip to content

Fixes#581

Open
FireBurn wants to merge 3 commits into
DisplayLink:mainfrom
FireBurn:fixes
Open

Fixes#581
FireBurn wants to merge 3 commits into
DisplayLink:mainfrom
FireBurn:fixes

Conversation

@FireBurn

@FireBurn FireBurn commented Jun 30, 2026

Copy link
Copy Markdown

EVDI PR notes — drm-next (7.1) build support + USB-unplug teardown fix

PR-ready material for DisplayLink/evdi. Branch fix-usb-unplug-teardown
(in evdi/, based on origin/main) carries three clean, signed-off commits:

# Commit What
1 evdi: support the drm_atomic_state -> drm_atomic_commit rename builds evdi against Linux 7.1 / drm-next
2 evdi: react to USB_DEVICE_REMOVE in the USB unplug notifier unplug bug #1
3 evdi: destroy dynamic devices when their USB parent is removed unplug bug #2

Commit 1 is first so the tree builds on a 7.1 kernel before the behavioural
commits (bisectable on every kernel; the shim is inert below 7.1).


1. drm-next (Linux 7.1) build support

drm-next renamed the global atomic-state object and its allocator helpers:

struct drm_atomic_state          -> struct drm_atomic_commit
drm_atomic_state_alloc/clear/put -> drm_atomic_commit_alloc/clear/put

The atomic-helper vtable callbacks (.atomic_flush/.atomic_update/...) and the
state accessors (drm_atomic_get_{new,old}_*_state()) now take a
struct drm_atomic_commit *. evdi still uses the historical drm_atomic_state
names throughout evdi_modeset.c, so against >= 7.1 it fails with incompatible
pointer / function-pointer types.

The commit aliases the old identifiers to the new ones for
KERNEL_VERSION(7, 1, 0)+ in evdi_drm_drv.h. Because evdi refers to the struct
and the three renamed helpers by name everywhere, a handful of whole-token
#defines cascade through the whole module; the old names are gone from the
tree, so the macros cannot collide. Authored by Mike Lothian (predates this
work).

2 + 3. USB-unplug teardown

When the dock was physically removed, evdi's USB-removal notifier
(evdi_platform_drv_usb()) was supposed to unlink and destroy the
dynamically-created evdi platform/DRM device. It never did — drm_dev_unplug()
never ran, no remove uevent was emitted, the DRM node lingered as a ghost, and
/sys/module/evdi/refcnt climbed and never fell (reboot required). Wayland
compositors (KWin) and Xorg, which rely on the hot-unplug uevent, were left
holding a stale fd.

Two independent bugs, either alone enough to block teardown — so the path had
never worked:

  1. Wrong notifier action constant (commit 2). The notifier is installed with
    usb_register_notify(), so the USB core delivers USB_DEVICE_ADD /
    USB_DEVICE_REMOVE (drivers/usb/core/notify.c). The handler instead tested
    action != BUS_NOTIFY_DEL_DEVICE, and BUS_NOTIFY_DEL_DEVICE == 1 == USB_DEVICE_ADD, so the body ran on every add and returned early on every
    remove — it never reacted to an unplug. Fixed by testing
    USB_DEVICE_REMOVE.
  2. Dead destroy branch (commit 3). Even once the handler ran, destroy was
    gated on pdev->dev.parent == &usb_dev->dev, but evdi platform devices are
    created with .parent = NULL (evdi_platform_drv_create_new_device()); the
    USB link lives in evdi_platform_device_data::parent. The condition was never
    true. Fixed by making evdi_platform_device_unlink_if_linked_with() return
    bool (did it detach from this parent) and destroying when it did,
    restricted to dynamic devices (i >= evdi_initial_device_count) so the
    statically pre-allocated pool (for static-device-list clients like Xorg) is
    only detached.

Teardown chain restored: evdi_platform_dev_destroy()
platform_device_unregister()evdi_platform_device_remove()
evdi_drm_device_remove()drm_dev_unplug() → udev remove uevent.

Verification (HW, 2026-06-30)

Dell D6000 dock, kernel 7.1.0-rc5-drm+, initial_device_count=0, KWin/Wayland +
DisplayLinkManager driving two heads.

  • Before: unplug produced only change uevents; evdi.0/evdi.1 and their
    device symlinks persisted; refcnt stuck (23 → 23).
  • After: dmesg Detached from parent deviceParent USB removed. Removing evdi.0/evdi.1Evdi platform_device destroyEvdi drm_device removed.;
    udev remove (not just change) on card2/card3 + connectors;
    /sys/devices/platform/evdi.* gone; /dev/dri/ back to the real GPUs;
    refcnt 23 → 0 (no leak).

module/evdi.ko builds clean across all three commits (sha verified identical to
the tested module).


checkpatch (--strict): 0 errors on all three

Remaining items are non-blocking:

  • Commit 1: LINUX_VERSION_CODE should be avoided — inherent to any kernel
    version-compat shim; evdi is an out-of-tree module built on KERNEL_VERSION()
    guards throughout.
  • Commits 2 & 3: Co-authored-by: is not a kernel-standard trailer (the kernel
    recognises Co-developed-by:, which needs its own DCO sign-off). Drop the
    trailer if a spotless run is wanted.
  • Commit 3: 2 advisory CHECKs ("Alignment should match open parenthesis") on the
    evdi_platform_device_unlink_if_linked_with signature — pre-existing upstream
    indentation; the patch only changes the return keyword (voidbool, same
    width), introducing no new misalignment.

FireBurn and others added 3 commits June 30, 2026 14:56
drm-next (Linux 7.1) renamed the global atomic-state object and its
allocator helpers:

  struct drm_atomic_state          -> struct drm_atomic_commit
  drm_atomic_state_alloc/clear/put -> drm_atomic_commit_alloc/clear/put

The atomic-helper vtable callbacks (.atomic_flush/.atomic_update/...) and
the state accessors (drm_atomic_get_{new,old}_*_state()) now take a
struct drm_atomic_commit *. evdi still uses the historical drm_atomic_state
names throughout evdi_modeset.c, so it fails to build against >= 7.1 with
incompatible pointer and function-pointer types.

Alias the old identifiers to the new ones for KERNEL_VERSION(7, 1, 0) and
later. The old names are fully gone from the tree, so these whole-token
macros cannot collide with anything still in the headers.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
evdi_platform_drv_usb() is installed with usb_register_notify(), so the
USB core delivers it the USB_DEVICE_ADD / USB_DEVICE_REMOVE notifications
(see usb_notify_add_device()/usb_notify_remove_device() in
drivers/usb/core/notify.c), with the struct usb_device passed in @DaTa.

The handler instead compared @action against the bus-notifier constant
BUS_NOTIFY_DEL_DEVICE. That enum value happens to equal USB_DEVICE_ADD (1),
so the notifier body ran on every USB device *addition* and returned early
on every *removal*. As a result evdi never reacted to its parent USB device
(the dock) being unplugged.

Test the correct USB_DEVICE_REMOVE constant so the handler runs when the
parent USB device is removed.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
When the parent USB device was unplugged the removal notifier detached the
evdi platform device from it but never destroyed it. The teardown was gated
on:

	if (pdev->dev.parent == &usb_dev->dev)

but evdi platform devices are created with .parent = NULL (see
evdi_platform_drv_create_new_device()); the USB relationship is tracked in
evdi_platform_device_data::parent, not in dev.parent. The condition was
therefore never true, so the DRM device was never unregistered,
drm_dev_unplug() never ran, and userspace (Wayland compositors, Xorg)
received no hot-unplug uevent and leaked the stale device.

Have evdi_platform_device_unlink_if_linked_with() report whether it
actually detached the device from this USB parent, and destroy the device
when it did. Restrict the destroy to dynamically created devices
(i >= evdi_initial_device_count); the statically pre-allocated devices are
only detached, preserving their intended lifetime for static-list clients.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant