1package drm
2
3import (
4 "os"
5 "time"
6
7 "golang.org/x/sys/unix"
8)
9
10// Fence holds: client buffers the GPU may still read.
11
12// dupFences duplicates the non-nil fences.
13func dupFences(fs ...*os.File) []*os.File {
14 var out []*os.File
15 for _, f := range fs {
16 if d := dupFence(f); d != nil {
17 out = append(out, d)
18 }
19 }
20 return out
21}
22
23// signalled reports whether every fence signalled (readable); an fd that
24// cannot be polled counts as signalled.
25func signalled(fs []*os.File) bool {
26 if len(fs) == 0 {
27 return true
28 }
29 pfds := make([]unix.PollFd, len(fs))
30 for i, f := range fs {
31 pfds[i] = unix.PollFd{Fd: int32(f.Fd()), Events: unix.POLLIN}
32 }
33 if _, err := unix.Poll(pfds, 0); err != nil {
34 return true
35 }
36 for _, p := range pfds {
37 if p.Revents == 0 {
38 return false
39 }
40 }
41 return true
42}
43
44// holdRead keeps the fences of a frame that was not committed. Reap old
45// completed reads even when no content publication asks for a report.
46func (o *Output) holdRead(fs ...*os.File) {
47 o.readDone()
48 o.readFences = append(o.readFences, dupFences(fs...)...)
49}
50
51// readDone reports whether no uncommitted frame may still read client
52// buffers, closing the fences that signalled.
53func (o *Output) readDone() bool {
54 kept := o.readFences[:0]
55 for _, f := range o.readFences {
56 if signalled([]*os.File{f}) {
57 f.Close()
58 continue
59 }
60 kept = append(kept, f)
61 }
62 clear(o.readFences[len(kept):])
63 o.readFences = kept
64 return len(kept) == 0
65}
66
67// dropRead closes the fences of uncommitted frames.
68func (o *Output) dropRead() {
69 for _, f := range o.readFences {
70 f.Close()
71 }
72 o.readFences = nil
73}
74
75// expire applies a lifecycle deadline and reports whether KMS needs a modeset.
76// Fence polling and logging stay at the output boundary, not in the value
77// transition; the lifecycle owns the decision and closes pending fences.
78func (o *Output) expire() bool {
79 now := time.Now()
80 serial, frame, age, busy, fences := o.frame.deadlineInfo(now)
81 ready := serial == 0 || !o.frame.deadlineReached(now) || signalled(fences)
82 switch o.frame.timeout(now, ready) {
83 case timeoutFence:
84 o.log.Warn().Str("connector", o.conn.name).Uint64("serial", serial).Dur("age", age).Msg("frame waits for GPU fence")
85 case timeoutBusy:
86 o.log.Warn().Str("connector", o.conn.name).Str("kind", "busy").Dur("age", busy).Msg("commits refused as busy; modeset")
87 return true
88 case timeoutMissing:
89 kind := "state"
90 if frame {
91 kind = "frame"
92 }
93 o.log.Warn().Str("connector", o.conn.name).Uint64("serial", serial).Str("kind", kind).Dur("age", age).Msg("commit event missing; modeset")
94 return true
95 }
96 return false
97}