@@ -31,70 +31,6 @@ import (
31
31
"golang.org/x/sys/unix"
32
32
)
33
33
34
- // odirectReader - to support O_DIRECT reads for erasure backends.
35
- type odirectReader struct {
36
- fd int
37
- Bufp * []byte
38
- buf []byte
39
- err error
40
- seenRead bool
41
- alignment bool
42
-
43
- ctx context.Context
44
- }
45
-
46
- // Read - Implements Reader interface.
47
- func (o * odirectReader ) Read (buf []byte ) (n int , err error ) {
48
- if o .ctx .Err () != nil {
49
- return 0 , o .ctx .Err ()
50
- }
51
- if o .err != nil && (len (o .buf ) == 0 || ! o .seenRead ) {
52
- return 0 , o .err
53
- }
54
- if ! o .seenRead {
55
- o .buf = * o .Bufp
56
- n , err = syscall .Read (o .fd , o .buf )
57
- if err != nil && err != io .EOF {
58
- if errors .Is (err , syscall .EINVAL ) {
59
- if err = disableDirectIO (uintptr (o .fd )); err != nil {
60
- o .err = err
61
- return n , err
62
- }
63
- n , err = syscall .Read (o .fd , o .buf )
64
- }
65
- if err != nil && err != io .EOF {
66
- o .err = err
67
- return n , err
68
- }
69
- }
70
- if n == 0 {
71
- if err == nil {
72
- err = io .EOF
73
- }
74
- o .err = err
75
- return n , err
76
- }
77
- o .err = err
78
- o .buf = o .buf [:n ]
79
- o .seenRead = true
80
- }
81
- if len (buf ) >= len (o .buf ) {
82
- n = copy (buf , o .buf )
83
- o .seenRead = false
84
- return n , o .err
85
- }
86
- n = copy (buf , o .buf )
87
- o .buf = o .buf [n :]
88
- // There is more left in buffer, do not return any EOF yet.
89
- return n , nil
90
- }
91
-
92
- // Close - Release the buffer and close the file.
93
- func (o * odirectReader ) Close () error {
94
- o .err = errors .New ("internal error: odirectReader Read after Close" )
95
- return syscall .Close (o .fd )
96
- }
97
-
98
34
type nullWriter struct {}
99
35
100
36
func (n nullWriter ) Write (b []byte ) (int , error ) {
@@ -103,21 +39,14 @@ func (n nullWriter) Write(b []byte) (int, error) {
103
39
104
40
func (d * DrivePerf ) runReadTest (ctx context.Context , path string , data []byte ) (uint64 , error ) {
105
41
startTime := time .Now ()
106
- fd , err := syscall . Open (path , syscall .O_DIRECT | syscall .O_RDONLY , 0o400 )
42
+ r , err := os . OpenFile (path , syscall .O_DIRECT | os .O_RDONLY , 0o400 )
107
43
if err != nil {
108
44
return 0 , err
109
45
}
110
- unix .Fadvise (fd , 0 , int64 (d .FileSize ), unix .FADV_SEQUENTIAL )
46
+ unix .Fadvise (int ( r . Fd ()) , 0 , int64 (d .FileSize ), unix .FADV_SEQUENTIAL )
111
47
112
- of := & odirectReader {
113
- fd : fd ,
114
- Bufp : & data ,
115
- ctx : ctx ,
116
- alignment : d .FileSize % 4096 == 0 ,
117
- }
118
-
119
- n , err := io .Copy (& nullWriter {}, of )
120
- of .Close ()
48
+ n , err := copyAligned (& nullWriter {}, r , data , int64 (d .FileSize ), r .Fd ())
49
+ r .Close ()
121
50
if err != nil {
122
51
return 0 , err
123
52
}
@@ -164,7 +93,7 @@ func (n nullReader) Read(b []byte) (int, error) {
164
93
return len (b ), nil
165
94
}
166
95
167
- func newEncReader (ctx context.Context ) io.Reader {
96
+ func newRandomReader (ctx context.Context ) io.Reader {
168
97
r , err := rng .NewReader ()
169
98
if err != nil {
170
99
panic (err )
@@ -183,73 +112,108 @@ func disableDirectIO(fd uintptr) error {
183
112
return err
184
113
}
185
114
186
- func copyAligned (fd int , r io.Reader , alignedBuf []byte , totalSize int64 ) (written int64 , err error ) {
187
- defer func () {
188
- ferr := fdatasync (fd )
189
- if ferr != nil {
190
- // preserve error on fdatasync
191
- err = ferr
192
- }
193
- cerr := syscall .Close (fd )
194
- if cerr != nil {
195
- // preserve error on close
196
- err = cerr
197
- }
198
- }()
199
-
200
- // Writes remaining bytes in the buffer.
201
- writeUnaligned := func (buf []byte ) (remainingWritten int64 , err error ) {
202
- // Disable O_DIRECT on fd's on unaligned buffer
203
- // perform an amortized Fdatasync(fd) on the fd at
204
- // the end, this is performed by the caller before
205
- // closing 'w'.
206
- if err = disableDirectIO (uintptr (fd )); err != nil {
207
- return remainingWritten , err
208
- }
209
- n , err := syscall .Write (fd , buf )
210
- return int64 (n ), err
115
+ // DirectioAlignSize - DirectIO alignment needs to be 4K. Defined here as
116
+ // directio.AlignSize is defined as 0 in MacOS causing divide by 0 error.
117
+ const DirectioAlignSize = 4096
118
+
119
+ // copyAligned - copies from reader to writer using the aligned input
120
+ // buffer, it is expected that input buffer is page aligned to
121
+ // 4K page boundaries. Without passing aligned buffer may cause
122
+ // this function to return error.
123
+ //
124
+ // This code is similar in spirit to io.Copy but it is only to be
125
+ // used with DIRECT I/O based file descriptor and it is expected that
126
+ // input writer *os.File not a generic io.Writer. Make sure to have
127
+ // the file opened for writes with syscall.O_DIRECT flag.
128
+ func copyAligned (w io.Writer , r io.Reader , alignedBuf []byte , totalSize int64 , fd uintptr ) (int64 , error ) {
129
+ if totalSize == 0 {
130
+ return 0 , nil
211
131
}
212
132
133
+ var written int64
213
134
for {
214
135
buf := alignedBuf
215
- if totalSize != - 1 {
136
+ if totalSize > 0 {
216
137
remaining := totalSize - written
217
138
if remaining < int64 (len (buf )) {
218
139
buf = buf [:remaining ]
219
140
}
220
141
}
142
+
143
+ if len (buf )% DirectioAlignSize != 0 {
144
+ // Disable O_DIRECT on fd's on unaligned buffer
145
+ // perform an amortized Fdatasync(fd) on the fd at
146
+ // the end, this is performed by the caller before
147
+ // closing 'w'.
148
+ if err := disableDirectIO (fd ); err != nil {
149
+ return written , err
150
+ }
151
+ }
152
+
221
153
nr , err := io .ReadFull (r , buf )
222
- eof := err == io .EOF || err == io .ErrUnexpectedEOF
154
+ eof := errors . Is ( err , io .EOF ) || errors . Is ( err , io .ErrUnexpectedEOF )
223
155
if err != nil && ! eof {
224
156
return written , err
225
157
}
158
+
226
159
buf = buf [:nr ]
227
- var nw int64
228
- if len (buf )% 4096 == 0 {
229
- var n int
160
+ var (
161
+ n int
162
+ un int
163
+ nw int64
164
+ )
165
+
166
+ remain := len (buf ) % DirectioAlignSize
167
+ if remain == 0 {
230
168
// buf is aligned for directio write()
231
- n , err = syscall .Write (fd , buf )
169
+ n , err = w .Write (buf )
232
170
nw = int64 (n )
233
171
} else {
172
+ if remain < len (buf ) {
173
+ n , err = w .Write (buf [:len (buf )- remain ])
174
+ if err != nil {
175
+ return written , err
176
+ }
177
+ nw = int64 (n )
178
+ }
179
+
180
+ // Disable O_DIRECT on fd's on unaligned buffer
181
+ // perform an amortized Fdatasync(fd) on the fd at
182
+ // the end, this is performed by the caller before
183
+ // closing 'w'.
184
+ if err = disableDirectIO (fd ); err != nil {
185
+ return written , err
186
+ }
187
+
234
188
// buf is not aligned, hence use writeUnaligned()
235
- nw , err = writeUnaligned (buf )
189
+ // for the remainder
190
+ un , err = w .Write (buf [len (buf )- remain :])
191
+ nw += int64 (un )
236
192
}
193
+
237
194
if nw > 0 {
238
195
written += nw
239
196
}
197
+
240
198
if err != nil {
241
199
return written , err
242
200
}
201
+
243
202
if nw != int64 (len (buf )) {
244
203
return written , io .ErrShortWrite
245
204
}
246
205
247
- if totalSize != - 1 {
248
- if written == totalSize {
249
- return written , nil
250
- }
206
+ if totalSize > 0 && written == totalSize {
207
+ // we have written the entire stream, return right here.
208
+ return written , nil
251
209
}
210
+
252
211
if eof {
212
+ // We reached EOF prematurely but we did not write everything
213
+ // that we promised that we would write.
214
+ if totalSize > 0 && written != totalSize {
215
+ return written , io .ErrUnexpectedEOF
216
+ }
253
217
return written , nil
254
218
}
255
219
}
@@ -261,20 +225,30 @@ func (d *DrivePerf) runWriteTest(ctx context.Context, path string, data []byte)
261
225
}
262
226
263
227
startTime := time .Now ()
264
- fd , err := syscall . Open (path , syscall .O_DIRECT | syscall .O_RDWR | syscall . O_CREAT | syscall .O_TRUNC , 0o600 )
228
+ w , err := os . OpenFile (path , syscall .O_DIRECT | os .O_RDWR | os . O_CREATE | os .O_TRUNC , 0o600 )
265
229
if err != nil {
266
230
return 0 , err
267
231
}
268
232
269
- n , err := copyAligned (fd , newEncReader (ctx ), data , int64 (d .FileSize ))
233
+ n , err := copyAligned (w , newRandomReader (ctx ), data , int64 (d .FileSize ), w . Fd ( ))
270
234
if err != nil {
235
+ w .Close ()
271
236
return 0 , err
272
237
}
273
238
274
239
if n != int64 (d .FileSize ) {
240
+ w .Close ()
275
241
return 0 , fmt .Errorf ("Expected to write %d, wrote %d bytes" , d .FileSize , n )
276
242
}
277
243
244
+ if err := fdatasync (int (w .Fd ())); err != nil {
245
+ return 0 , err
246
+ }
247
+
248
+ if err := w .Close (); err != nil {
249
+ return 0 , err
250
+ }
251
+
278
252
dt := float64 (time .Since (startTime ))
279
253
throughputInSeconds := (float64 (d .FileSize ) / dt ) * float64 (time .Second )
280
254
return uint64 (throughputInSeconds ), nil
0 commit comments