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

1 participant