# DF-4197 — DragonFly's Linux-compat seqlock shim violates the seqcount protocol → torn vblank {count,time} snapshots accepted (drm_vblank_count_and_time)

File under audit: `sys/dev/drm/drm_vblank.c` (pass 2)

## What

`sys/dev/drm/include/linux/seqlock.h` implements the Linux seqlock API with
broken seqcount semantics:

```c
write_seqlock()   { spin_lock; sl->sequence++;  sfence; }   /* 0 -> 1 */
write_sequnlock() { sl->sequence--;  spin_unlock;  sfence; }/* 1 -> 0  ← DECREMENT */
read_seqbegin()   { return READ_ONCE(sl->sequence); }       /* no wait-even */
read_seqretry()   { lfence; return sl->sequence != start; }
```

A Linux seqcount **advances monotonically** (each write section bumps the
count twice: 0→1→2→4→…), so any writer that started after a reader's
`read_seqcount_begin()` changes the count and forces a retry. The DF shim
returns the count to its **original value** (0→1→0). A reader that starts
before a writer and re-checks after that writer completes observes
`sequence == start` and **accepts a torn snapshot** — precisely the race the
seqcount protocol exists to detect.

The in-kernel consumer under audit is the vblank counter/timestamp pair:

* writer: `store_vblank()` — drm_vblank.c:92-106
  (`write_seqlock; vblank->time = t; vblank->count += inc; write_sequnlock`),
* reader: `drm_vblank_count_and_time()` — drm_vblank.c:784-788
  (`do { seq = read_seqbegin; count = vblank->count; time = vblank->time; }
  while (read_seqretry)`).

A torn {count,time} pair is handed to userspace by
`drm_wait_vblank_ioctl` (reply.sequence/tval), `drm_crtc_get_sequence_ioctl`
(:1801-1802) and `drm_crtc_queue_sequence_ioctl` (:1861, event target
sequence), and used internally by `drm_crtc_send_vblank_event` and page-flip
completion timestamps. Result: wrong vblank sequence numbers/timestamps
(event delivered against a mismatched counter, waits satisfied early/never)
— logic corruption of the UAPI vblank contract, no memory unsafety. On i386
builds the unlocked u64 counter reads (`drm_vblank_count`, :277-285) can
tear independently of the seqlock.

Second in-tree consumer of the same shim (same loss of guarantee):
i915 engine stats (`intel_engine_cs.c:1623` write_seqlock cycles).

## How it is proven here

`seqlock_aba.c` (guest, DragonFly cc 8.3, -O2):

* `demo` — deterministic single-thread interleave of the exact shim
  functions (verbatim semantics from seqlock.h:69-99): reader begins
  (seq=0), loads OLD count, writer runs a COMPLETE write section
  (0→1→0), reader loads NEW time, `read_seqretry` returns 0 →
  **TORN PAIR {count=100(old), time=200000(new)} ACCEPTED**.
* `demo_fixed` — identical interleave with corrected (Linux monotonic)
  semantics: sequence ends at 2, retry fires, torn pair rejected.
* `stress` — real 2-thread contention, 4M write cycles:
  **DF semantics: torn_accepted=4057/4189 (two runs)**;
  **fixed semantics: torn_accepted=0**.

## Reproduce

```
cc -O2 -Wall -pthread -o seqlock_aba seqlock_aba.c
./seqlock_aba demo          # TORN PAIR ACCEPTED, rc=1
./seqlock_aba demo_fixed    # torn rejected, rc=0
./seqlock_aba stress        # torn_accepted=4057, rc=1
./seqlock_aba stress_fixed  # torn_accepted=0, rc=0
```

## Scope

Kernel-level manifestation needs a DRM device (guest has none — hardware
gated), but the shim defect itself is proven exactly: the harness copies the
shim's algorithm 1:1. The fix (`fix.diff`) restores Linux seqcount semantics
in `sys/dev/drm/include/linux/seqlock.h`.
