@@ -31,70 +31,6 @@ import (
3131 "golang.org/x/sys/unix"
3232)
3333
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-
9834type nullWriter struct {}
9935
10036func (n nullWriter ) Write (b []byte ) (int , error ) {
@@ -103,21 +39,14 @@ func (n nullWriter) Write(b []byte) (int, error) {
10339
10440func (d * DrivePerf ) runReadTest (ctx context.Context , path string , data []byte ) (uint64 , error ) {
10541 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 )
10743 if err != nil {
10844 return 0 , err
10945 }
110- unix .Fadvise (fd , 0 , int64 (d .FileSize ), unix .FADV_SEQUENTIAL )
46+ unix .Fadvise (int ( r . Fd ()) , 0 , int64 (d .FileSize ), unix .FADV_SEQUENTIAL )
11147
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 ()
12150 if err != nil {
12251 return 0 , err
12352 }
@@ -164,7 +93,7 @@ func (n nullReader) Read(b []byte) (int, error) {
16493 return len (b ), nil
16594}
16695
167- func newEncReader (ctx context.Context ) io.Reader {
96+ func newRandomReader (ctx context.Context ) io.Reader {
16897 r , err := rng .NewReader ()
16998 if err != nil {
17099 panic (err )
@@ -183,73 +112,108 @@ func disableDirectIO(fd uintptr) error {
183112 return err
184113}
185114
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
211131 }
212132
133+ var written int64
213134 for {
214135 buf := alignedBuf
215- if totalSize != - 1 {
136+ if totalSize > 0 {
216137 remaining := totalSize - written
217138 if remaining < int64 (len (buf )) {
218139 buf = buf [:remaining ]
219140 }
220141 }
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+
221153 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 )
223155 if err != nil && ! eof {
224156 return written , err
225157 }
158+
226159 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 {
230168 // buf is aligned for directio write()
231- n , err = syscall .Write (fd , buf )
169+ n , err = w .Write (buf )
232170 nw = int64 (n )
233171 } 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+
234188 // 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 )
236192 }
193+
237194 if nw > 0 {
238195 written += nw
239196 }
197+
240198 if err != nil {
241199 return written , err
242200 }
201+
243202 if nw != int64 (len (buf )) {
244203 return written , io .ErrShortWrite
245204 }
246205
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
251209 }
210+
252211 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+ }
253217 return written , nil
254218 }
255219 }
@@ -261,20 +225,30 @@ func (d *DrivePerf) runWriteTest(ctx context.Context, path string, data []byte)
261225 }
262226
263227 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 )
265229 if err != nil {
266230 return 0 , err
267231 }
268232
269- n , err := copyAligned (fd , newEncReader (ctx ), data , int64 (d .FileSize ))
233+ n , err := copyAligned (w , newRandomReader (ctx ), data , int64 (d .FileSize ), w . Fd ( ))
270234 if err != nil {
235+ w .Close ()
271236 return 0 , err
272237 }
273238
274239 if n != int64 (d .FileSize ) {
240+ w .Close ()
275241 return 0 , fmt .Errorf ("Expected to write %d, wrote %d bytes" , d .FileSize , n )
276242 }
277243
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+
278252 dt := float64 (time .Since (startTime ))
279253 throughputInSeconds := (float64 (d .FileSize ) / dt ) * float64 (time .Second )
280254 return uint64 (throughputInSeconds ), nil
0 commit comments