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.
45func (o *Output) holdRead(fs ...*os.File) {
46 o.readFences = append(o.readFences, dupFences(fs...)...)
47}
48
49// readDone reports whether no uncommitted frame may still read client
50// buffers, closing the fences that signalled.
51func (o *Output) readDone() bool {
52 kept := o.readFences[:0]
53 for _, f := range o.readFences {
54 if signalled([]*os.File{f}) {
55 f.Close()
56 continue
57 }
58 kept = append(kept, f)
59 }
60 clear(o.readFences[len(kept):])
61 o.readFences = kept
62 return len(kept) == 0
63}
64
65// dropRead closes the fences of uncommitted frames.
66func (o *Output) dropRead() {
67 for _, f := range o.readFences {
68 f.Close()
69 }
70 o.readFences = nil
71}
72
73// expire applies a lifecycle deadline and reports whether KMS needs a modeset.
74// Fence polling and logging stay at the output boundary, not in the value
75// transition; the lifecycle owns the decision and closes pending fences.
76func (o *Output) expire() bool {
77 now := time.Now()
78 serial, frame, age, busy, fences := o.frame.deadlineInfo(now)
79 ready := serial == 0 || !o.frame.deadlineReached(now) || signalled(fences)
80 switch o.frame.timeout(now, ready) {
81 case timeoutFence:
82 o.log.Warn().Str("connector", o.conn.name).Uint64("serial", serial).Dur("age", age).Msg("frame waits for GPU fence")
83 case timeoutBusy:
84 o.log.Warn().Str("connector", o.conn.name).Str("kind", "busy").Dur("age", busy).Msg("commits refused as busy; modeset")
85 return true
86 case timeoutMissing:
87 kind := "state"
88 if frame {
89 kind = "frame"
90 }
91 o.log.Warn().Str("connector", o.conn.name).Uint64("serial", serial).Str("kind", kind).Dur("age", age).Msg("commit event missing; modeset")
92 return true
93 }
94 return false
95}