Source file src/pkg/internal/poll/splice_linux.go
1
2
3
4
5 package poll
6
7 import (
8 "sync/atomic"
9 "syscall"
10 "unsafe"
11 )
12
13 const (
14
15 spliceNonblock = 0x2
16
17
18
19 maxSpliceSize = 4 << 20
20 )
21
22
23
24
25
26
27
28
29 func Splice(dst, src *FD, remain int64) (written int64, handled bool, sc string, err error) {
30 prfd, pwfd, sc, err := newTempPipe()
31 if err != nil {
32 return 0, false, sc, err
33 }
34 defer destroyTempPipe(prfd, pwfd)
35 var inPipe, n int
36 for err == nil && remain > 0 {
37 max := maxSpliceSize
38 if int64(max) > remain {
39 max = int(remain)
40 }
41 inPipe, err = spliceDrain(pwfd, src, max)
42
43
44
45
46
47
48
49
50
51
52
53 handled = handled || (err != syscall.EINVAL)
54 if err != nil || (inPipe == 0 && err == nil) {
55 break
56 }
57 n, err = splicePump(dst, prfd, inPipe)
58 if n > 0 {
59 written += int64(n)
60 remain -= int64(n)
61 }
62 }
63 if err != nil {
64 return written, handled, "splice", err
65 }
66 return written, true, "", nil
67 }
68
69
70
71
72
73
74
75
76
77
78
79 func spliceDrain(pipefd int, sock *FD, max int) (int, error) {
80 if err := sock.readLock(); err != nil {
81 return 0, err
82 }
83 defer sock.readUnlock()
84 if err := sock.pd.prepareRead(sock.isFile); err != nil {
85 return 0, err
86 }
87 for {
88 n, err := splice(pipefd, sock.Sysfd, max, spliceNonblock)
89 if err != syscall.EAGAIN {
90 return n, err
91 }
92 if err := sock.pd.waitRead(sock.isFile); err != nil {
93 return n, err
94 }
95 }
96 }
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 func splicePump(sock *FD, pipefd int, inPipe int) (int, error) {
112 if err := sock.writeLock(); err != nil {
113 return 0, err
114 }
115 defer sock.writeUnlock()
116 if err := sock.pd.prepareWrite(sock.isFile); err != nil {
117 return 0, err
118 }
119 written := 0
120 for inPipe > 0 {
121 n, err := splice(sock.Sysfd, pipefd, inPipe, spliceNonblock)
122
123
124 if n > 0 {
125 inPipe -= n
126 written += n
127 continue
128 }
129 if err != syscall.EAGAIN {
130 return written, err
131 }
132 if err := sock.pd.waitWrite(sock.isFile); err != nil {
133 return written, err
134 }
135 }
136 return written, nil
137 }
138
139
140
141
142
143 func splice(out int, in int, max int, flags int) (int, error) {
144 n, err := syscall.Splice(in, nil, out, nil, max, flags)
145 return int(n), err
146 }
147
148 var disableSplice unsafe.Pointer
149
150
151 func newTempPipe() (prfd, pwfd int, sc string, err error) {
152 p := (*bool)(atomic.LoadPointer(&disableSplice))
153 if p != nil && *p {
154 return -1, -1, "splice", syscall.EINVAL
155 }
156
157 var fds [2]int
158
159
160
161
162 const flags = syscall.O_CLOEXEC | syscall.O_NONBLOCK
163 if err := syscall.Pipe2(fds[:], flags); err != nil {
164 return -1, -1, "pipe2", err
165 }
166
167 if p == nil {
168 p = new(bool)
169 defer atomic.StorePointer(&disableSplice, unsafe.Pointer(p))
170
171
172 if _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fds[0]), syscall.F_GETPIPE_SZ, 0); errno != 0 {
173 *p = true
174 destroyTempPipe(fds[0], fds[1])
175 return -1, -1, "fcntl", errno
176 }
177 }
178
179 return fds[0], fds[1], "", nil
180 }
181
182
183 func destroyTempPipe(prfd, pwfd int) error {
184 err := CloseFunc(prfd)
185 err1 := CloseFunc(pwfd)
186 if err == nil {
187 return err1
188 }
189 return err
190 }
191
View as plain text