@@ -582,8 +582,9 @@ impl ExtentInner for RawInner {
582582 }
583583
584584 fn validate ( & self ) -> Result < ( ) , CrucibleError > {
585- let block_size = self . extent_size . block_size_in_bytes ( ) as u64 ;
585+ let block_size = self . extent_size . block_size_in_bytes ( ) as usize ;
586586
587+ // Read context data to local arrays
587588 let ctx_a = self . layout . read_context_slots_contiguous (
588589 & self . file ,
589590 0 ,
@@ -597,41 +598,57 @@ impl ExtentInner for RawInner {
597598 ContextSlot :: B ,
598599 ) ?;
599600
600- for block in 0 ..self . extent_size . value {
601- // Read the block data itself:
602- let mut buf = vec ! [ 0 ; block_size as usize ] ;
603- pread_all ( self . file . as_fd ( ) , & mut buf, ( block_size * block) as i64 )
604- . map_err ( |e| {
605- CrucibleError :: IoError ( format ! (
606- "extent {}: reading block {block} data failed: {e}" ,
607- self . extent_number
608- ) )
609- } ) ?;
610- let hash = integrity_hash ( & [ & buf] ) ;
601+ // Read blocks in bulk, 128 KiB at a time
602+ let nblocks = 128 * 1024 / block_size;
603+ let mut buf = vec ! [ 0 ; block_size * nblocks] ;
604+ for start_block in ( 0 ..self . extent_size . value ) . step_by ( nblocks) {
605+ let num_blocks =
606+ ( ( self . extent_size . value - start_block) as usize ) . min ( nblocks) ;
611607
612- // Read the active context slot, which is by definition contiguous
613- let context = match self . active_context [ block] {
614- ContextSlot :: A => & ctx_a,
615- ContextSlot :: B => & ctx_b,
616- } [ block as usize ] ;
608+ // Read the block data itself:
609+ buf. resize ( num_blocks * block_size, 0u8 ) ;
610+ pread_all (
611+ self . file . as_fd ( ) ,
612+ & mut buf,
613+ block_size as i64 * start_block as i64 ,
614+ )
615+ . map_err ( |e| {
616+ CrucibleError :: IoError ( format ! (
617+ "extent {}: reading block {start_block} data failed: {e}" ,
618+ self . extent_number
619+ ) )
620+ } ) ?;
617621
618- if let Some ( context) = context {
619- if context. on_disk_hash == hash {
620- // great work, everyone
621- } else {
622- return Err ( CrucibleError :: GenericError ( format ! (
623- "block {block} has an active slot with mismatched hash"
624- ) ) ) ;
625- }
626- } else {
627- // context slot is empty, hopefully data is as well!
628- if buf. iter ( ) . all ( |v| * v == 0u8 ) {
629- // great work, everyone
622+ // Hash and check individual blocks against context slots
623+ for ( i, data) in buf. chunks_exact ( block_size) . enumerate ( ) {
624+ let block = start_block as usize + i;
625+ let hash = integrity_hash ( & [ data] ) ;
626+
627+ // Pick out the active context slot
628+ let context = match self . active_context [ block as u64 ] {
629+ ContextSlot :: A => & ctx_a,
630+ ContextSlot :: B => & ctx_b,
631+ } [ block] ;
632+
633+ if let Some ( context) = context {
634+ if context. on_disk_hash == hash {
635+ // great work, everyone
636+ } else {
637+ return Err ( CrucibleError :: GenericError ( format ! (
638+ "block {block} has an active slot \
639+ with mismatched hash"
640+ ) ) ) ;
641+ }
630642 } else {
631- return Err ( CrucibleError :: GenericError ( format ! (
632- "block {block} has an empty active slot, \
633- but contains none-zero data"
634- ) ) ) ;
643+ // context slot is empty, hopefully data is as well!
644+ if data. iter ( ) . all ( |v| * v == 0u8 ) {
645+ // great work, everyone
646+ } else {
647+ return Err ( CrucibleError :: GenericError ( format ! (
648+ "block {block} has an empty active slot, \
649+ but contains none-zero data",
650+ ) ) ) ;
651+ }
635652 }
636653 }
637654 }
0 commit comments