@@ -56,7 +56,10 @@ impl AlignedBuffer {
5656 let ptr = unsafe { std:: alloc:: alloc_zeroed ( layout) } ;
5757
5858 if ptr. is_null ( ) {
59- anyhow:: bail!( "Failed to allocate aligned buffer of {} bytes" , aligned_size) ;
59+ anyhow:: bail!(
60+ "Failed to allocate aligned buffer of {} bytes" ,
61+ aligned_size
62+ ) ;
6063 }
6164
6265 tracing:: trace!(
@@ -130,7 +133,8 @@ fn allocate_file(fd: RawFd, size: u64) -> anyhow::Result<()> {
130133 buf. len ( )
131134 } else {
132135 // Last partial write - round up to page size for O_DIRECT
133- let aligned = ( ( remaining as usize + PAGE_SIZE - 1 ) / PAGE_SIZE ) * PAGE_SIZE ;
136+ let aligned =
137+ ( ( remaining as usize + PAGE_SIZE - 1 ) / PAGE_SIZE ) * PAGE_SIZE ;
134138 std:: cmp:: min ( aligned, buf. len ( ) )
135139 } ;
136140
@@ -250,29 +254,88 @@ impl DiskStorage {
250254 let template = CString :: new ( file_path. to_str ( ) . unwrap ( ) ) . unwrap ( ) ;
251255 let mut template_bytes = template. into_bytes_with_nul ( ) ;
252256
257+ // mkostemp only supports flags like O_CLOEXEC, not O_RDWR/O_DIRECT.
258+ // The file is always opened O_RDWR by mkostemp.
259+ // We'll use fcntl to set O_DIRECT after creation.
260+ let raw_fd = unsafe {
261+ nix:: libc:: mkostemp (
262+ template_bytes. as_mut_ptr ( ) as * mut c_char ,
263+ nix:: libc:: O_CLOEXEC ,
264+ )
265+ } ;
266+
267+ if raw_fd < 0 {
268+ let file_name = CStr :: from_bytes_with_nul ( template_bytes. as_slice ( ) )
269+ . unwrap ( )
270+ . to_str ( )
271+ . unwrap_or ( "<invalid utf8>" ) ;
272+ return Err ( StorageError :: AllocationFailed ( format ! (
273+ "Failed to create temp file {}: {}" ,
274+ file_name,
275+ std:: io:: Error :: last_os_error( )
276+ ) ) ) ;
277+ }
278+
253279 // Determine whether to use O_DIRECT based on environment variable.
254280 // O_DIRECT is required for GPU DirectStorage but has strict alignment requirements.
255281 // For debugging or when filesystems don't support O_DIRECT alignment, it can be disabled.
256282 let disable_o_direct = std:: env:: var ( DISK_DISABLE_O_DIRECT_KEY ) . is_ok ( ) ;
257- let flags = if disable_o_direct {
258- tracing:: warn!(
259- "O_DIRECT disabled via {}. GPU DirectStorage performance may be reduced." ,
260- DISK_DISABLE_O_DIRECT_KEY
261- ) ;
262- nix:: libc:: O_RDWR
263- } else {
283+
284+ if !disable_o_direct {
285+ // Set O_DIRECT via fcntl after file creation.
264286 // For maximum performance, GPU DirectStorage requires O_DIRECT.
265287 // This allows transfers to bypass the kernel page cache.
266288 // It also introduces the restriction that all accesses must be page-aligned.
267- nix:: libc:: O_RDWR | nix:: libc:: O_DIRECT
268- } ;
289+ use nix:: fcntl:: { FcntlArg , OFlag , fcntl} ;
290+
291+ // Get current flags
292+ let current_flags = match fcntl ( raw_fd, FcntlArg :: F_GETFL ) {
293+ Ok ( flags) => OFlag :: from_bits_truncate ( flags) ,
294+ Err ( e) => {
295+ unsafe { nix:: libc:: close ( raw_fd) } ;
296+ let file_name = CStr :: from_bytes_with_nul ( template_bytes. as_slice ( ) )
297+ . unwrap ( )
298+ . to_str ( )
299+ . unwrap_or ( "<invalid utf8>" ) ;
300+ let _ = unlink ( file_name) ;
301+ return Err ( StorageError :: AllocationFailed ( format ! (
302+ "Failed to get file flags for {}: {}" ,
303+ file_name, e
304+ ) ) ) ;
305+ }
306+ } ;
269307
270- let raw_fd = unsafe {
271- nix:: libc:: mkostemp (
272- template_bytes. as_mut_ptr ( ) as * mut c_char ,
273- flags,
274- )
275- } ;
308+ // Add O_DIRECT to existing flags
309+ let new_flags = current_flags | OFlag :: O_DIRECT ;
310+
311+ if let Err ( e) = fcntl ( raw_fd, FcntlArg :: F_SETFL ( new_flags) ) {
312+ tracing:: error!(
313+ "Failed to set O_DIRECT on file descriptor {}: {}. \
314+ This may indicate filesystem doesn't support O_DIRECT. \
315+ Consider setting {}=true to disable O_DIRECT.",
316+ raw_fd,
317+ e,
318+ DISK_DISABLE_O_DIRECT_KEY
319+ ) ;
320+ unsafe { nix:: libc:: close ( raw_fd) } ;
321+ let file_name = CStr :: from_bytes_with_nul ( template_bytes. as_slice ( ) )
322+ . unwrap ( )
323+ . to_str ( )
324+ . unwrap_or ( "<invalid utf8>" ) ;
325+ let _ = unlink ( file_name) ;
326+ return Err ( StorageError :: AllocationFailed ( format ! (
327+ "Failed to set O_DIRECT: {}. Try {}=true" ,
328+ e, DISK_DISABLE_O_DIRECT_KEY
329+ ) ) ) ;
330+ }
331+
332+ tracing:: debug!( "O_DIRECT enabled for disk cache (fd={})" , raw_fd) ;
333+ } else {
334+ tracing:: warn!(
335+ "O_DIRECT disabled via {}. GPU DirectStorage performance may be reduced." ,
336+ DISK_DISABLE_O_DIRECT_KEY
337+ ) ;
338+ }
276339
277340 let file_name = CStr :: from_bytes_with_nul ( template_bytes. as_slice ( ) )
278341 . unwrap ( )
@@ -442,8 +505,7 @@ mod tests {
442505 ] ;
443506
444507 for requested_size in test_sizes {
445- let buf = AlignedBuffer :: new ( requested_size)
446- . expect ( "Failed to create aligned buffer" ) ;
508+ let buf = AlignedBuffer :: new ( requested_size) . expect ( "Failed to create aligned buffer" ) ;
447509
448510 let mut writer = StrictODirectWriter :: new ( ) ;
449511
@@ -487,24 +549,21 @@ mod tests {
487549 ) ;
488550 eprintln ! ( "✓ Confirmed: vec! buffer failed strict alignment check (as expected)" ) ;
489551 } else {
490- eprintln ! (
491- "⚠ vec! happened to be aligned this time (lucky!), but not guaranteed"
492- ) ;
552+ eprintln ! ( "⚠ vec! happened to be aligned this time (lucky!), but not guaranteed" ) ;
493553 }
494554 }
495555
496556 /// Test that the zero-fill write loop produces properly aligned write operations.
497557 #[ test]
498558 fn test_zerofill_write_loop_alignment ( ) {
499559 let test_sizes = vec ! [
500- 1_000_000 , // 1 MB non-aligned
501- 10_000_000 , // 10 MB non-aligned
502- 100_000_000 , // 100 MB non-aligned
560+ 1_000_000 , // 1 MB non-aligned
561+ 10_000_000 , // 10 MB non-aligned
562+ 100_000_000 , // 100 MB non-aligned
503563 ] ;
504564
505565 for total_size in test_sizes {
506- let buf = AlignedBuffer :: new ( ZERO_BUF_SIZE )
507- . expect ( "Failed to create buffer" ) ;
566+ let buf = AlignedBuffer :: new ( ZERO_BUF_SIZE ) . expect ( "Failed to create buffer" ) ;
508567
509568 let mut writer = StrictODirectWriter :: new ( ) ;
510569 let mut written: u64 = 0 ;
@@ -520,11 +579,14 @@ mod tests {
520579 } ;
521580
522581 // This should always succeed with our aligned buffer
523- writer. write ( & buf. as_slice ( ) [ ..to_write] )
524- . unwrap_or_else ( |e| panic ! (
525- "Write failed at offset {} for total size {}: {:?}" ,
526- written, total_size, e
527- ) ) ;
582+ writer
583+ . write ( & buf. as_slice ( ) [ ..to_write] )
584+ . unwrap_or_else ( |e| {
585+ panic ! (
586+ "Write failed at offset {} for total size {}: {:?}" ,
587+ written, total_size, e
588+ )
589+ } ) ;
528590
529591 written += to_write as u64 ;
530592 }
@@ -567,8 +629,9 @@ mod tests {
567629 for ( name, size) in test_cases {
568630 eprintln ! ( "Testing: {} ({} bytes)" , name, size) ;
569631
570- let storage = DiskStorage :: new ( size)
571- . unwrap_or_else ( |e| panic ! ( "Failed to allocate {} bytes ({}): {:?}" , size, name, e) ) ;
632+ let storage = DiskStorage :: new ( size) . unwrap_or_else ( |e| {
633+ panic ! ( "Failed to allocate {} bytes ({}): {:?}" , size, name, e)
634+ } ) ;
572635
573636 // Verify the file is actually the correct size
574637 assert_eq ! ( storage. size( ) , size, "Size mismatch for {}" , name) ;
@@ -583,7 +646,11 @@ mod tests {
583646 } ;
584647
585648 assert ! ( bytes_read > 0 , "No data read back for {}" , name) ;
586- assert ! ( buf. iter( ) . all( |& b| b == 0 ) , "File should be zero-filled for {}" , name) ;
649+ assert ! (
650+ buf. iter( ) . all( |& b| b == 0 ) ,
651+ "File should be zero-filled for {}" ,
652+ name
653+ ) ;
587654
588655 eprintln ! ( "✓ {} passed" , name) ;
589656 }
@@ -599,8 +666,7 @@ mod tests {
599666 std:: env:: set_var ( DISK_ZEROFILL_FALLBACK_KEY , "1" ) ;
600667
601668 let size = 1024 * 1024 ;
602- let storage = DiskStorage :: new ( size)
603- . expect ( "Failed to allocate with O_DIRECT disabled" ) ;
669+ let storage = DiskStorage :: new ( size) . expect ( "Failed to allocate with O_DIRECT disabled" ) ;
604670
605671 assert_eq ! ( storage. size( ) , size) ;
606672
0 commit comments