zircon/system/ulib/fs/journal/test/journal_test.cc
TEST_F(JournalTest, WriteToOverfilledJournalUpdatesInfoBlock) {
  storage::VmoBuffer metadata = registry()->InitializeBuffer(kJournalLength);

  const std::vector<storage::UnbufferedOperation> operations = {
      {
          zx::unowned_vmo(metadata.vmo().get()),
          {
              storage::OperationType::kWrite,
              .vmo_offset = 0,
              .dev_offset = 20,
              .length = 6,
          },
      },
      {
          zx::unowned_vmo(metadata.vmo().get()),
          {
              storage::OperationType::kWrite,
              .vmo_offset = 2,
              .dev_offset = 1234,
              .length = 1,
          },
      },
  };

  ASSERT_EQ(kJournalLength + 1,
            2 * kEntryMetadataBlocks + operations[0].op.length + operations[1].op.length,
            "Operations should just barely overfill the journal to cause info writeback");

  constexpr uint64_t kJournalStartBlock = 55;
  JournalRequestVerifier verifier(registry()->info(), registry()->journal(),
                                  registry()->writeback(), kJournalStartBlock);
  MockTransactionHandler::TransactionCallback callbacks[] = {
      [&](const std::vector<storage::BufferedOperation>& requests) {
        verifier.VerifyJournalWrite(operations[0], requests);
        return ZX_OK;
      },
      [&](const std::vector<storage::BufferedOperation>& requests) {
        verifier.VerifyMetadataWrite(operations[0], requests);
        verifier.ExtendJournalOffset(operations[0].op.length + kEntryMetadataBlocks);
        return ZX_OK;
      },
      [&](const std::vector<storage::BufferedOperation>& requests) {
        uint64_t sequence_number = 1;
        verifier.VerifyInfoBlockWrite(sequence_number, requests);
        registry()->VerifyReplay({}, sequence_number);
        return ZX_OK;
      },
      [&](const std::vector<storage::BufferedOperation>& requests) {
        verifier.VerifyJournalWrite(operations[1], requests);
        return ZX_OK;
      },
      [&](const std::vector<storage::BufferedOperation>& requests) {
        verifier.VerifyMetadataWrite(operations[1], requests);
        verifier.ExtendJournalOffset(operations[1].op.length + kEntryMetadataBlocks);

        // Before we update the info block, check that a power failure would result in
        // only the second metadata operation being replayed.
        //
        // The first operation has already completed and peristed thanks to the earlier
        // info block update.
        uint64_t sequence_number = 2;
        registry()->VerifyReplay({operations[1]}, sequence_number);
        return ZX_OK;
      },
      [&](const std::vector<storage::BufferedOperation>& requests) {
        uint64_t sequence_number = 2;
        verifier.VerifyInfoBlockWrite(sequence_number, requests);

        // After we update the info block, check that a power failure would result in
        // no operations being replayed - this equivalent to the "clean shutdown" case,
        // where there should be no work to do on reboot.
        registry()->VerifyReplay({}, sequence_number);
        return ZX_OK;
      },
  };
  MockTransactionHandler handler(registry(), callbacks, std::size(callbacks));
  {
    Journal journal(&handler, take_info(), take_journal_buffer(), take_data_buffer(),
                    kJournalStartBlock, Journal::Options());
    journal.schedule_task(journal.WriteMetadata({operations[0]}));
    journal.schedule_task(journal.WriteMetadata({operations[1]}));
  }
}
// Tests that the info block is updated if a metadata write would invalidate
// the entry pointed to by "start block".
//
// Operation 1: [ H, 1, 2, 3, 4, 5, 6, C, _, _ ]
//            : Info block update prompted by op 2.
// Operation 2: [ C, _, _, _, _, _, _, _, H, 1 ]
//            : Info block update prompted by termination.
Tests that the info block is updated if a metadata write would invalidate
the entry pointed to by "start block".


Tests that the info block is updated if a metadata write would invalidate the entry pointed to by " start block " .
TEST_F(JournalTest, WriteToOverfilledJournalUpdatesInfoBlock) {
  storage::VmoBuffer metadata = registry()->InitializeBuffer(kJournalLength);
  const std::vector<storage::UnbufferedOperation> operations = {
      {
          zx::unowned_vmo(metadata.vmo().get()),
          {
              storage::OperationType::kWrite,
              .vmo_offset = 0,
              .dev_offset = 20,
              .length = 6,
          },
      },
      {
          zx::unowned_vmo(metadata.vmo().get()),
          {
              storage::OperationType::kWrite,
              .vmo_offset = 2,
              .dev_offset = 1234,
              .length = 1,
          },
      },
  };
  ASSERT_EQ(kJournalLength + 1,
            2 * kEntryMetadataBlocks + operations[0].op.length + operations[1].op.length,
            "Operations should just barely overfill the journal to cause info writeback");
  constexpr uint64_t kJournalStartBlock = 55;
  JournalRequestVerifier verifier(registry()->info(), registry()->journal(),
                                  registry()->writeback(), kJournalStartBlock);
  MockTransactionHandler::TransactionCallback callbacks[] = {
      [&](const std::vector<storage::BufferedOperation>& requests) {
        verifier.VerifyJournalWrite(operations[0], requests);
        return ZX_OK;
      },
      [&](const std::vector<storage::BufferedOperation>& requests) {
        verifier.VerifyMetadataWrite(operations[0], requests);
        verifier.ExtendJournalOffset(operations[0].op.length + kEntryMetadataBlocks);
        return ZX_OK;
      },
      [&](const std::vector<storage::BufferedOperation>& requests) {
        uint64_t sequence_number = 1;
        verifier.VerifyInfoBlockWrite(sequence_number, requests);
        registry()->VerifyReplay({}, sequence_number);
        return ZX_OK;
      },
      [&](const std::vector<storage::BufferedOperation>& requests) {
        verifier.VerifyJournalWrite(operations[1], requests);
        return ZX_OK;
      },
      [&](const std::vector<storage::BufferedOperation>& requests) {
        verifier.VerifyMetadataWrite(operations[1], requests);
        verifier.ExtendJournalOffset(operations[1].op.length + kEntryMetadataBlocks);
         The first operation has already completed and peristed thanks to the earlier
         info block update.
        uint64_t sequence_number = 2;
        registry()->VerifyReplay({operations[1]}, sequence_number);
        return ZX_OK;
      },
      [&](const std::vector<storage::BufferedOperation>& requests) {
        uint64_t sequence_number = 2;
        verifier.VerifyInfoBlockWrite(sequence_number, requests);
         After we update the info block, check that a power failure would result in
         no operations being replayed - this equivalent to the "clean shutdown" case,
         where there should be no work to do on reboot.
        registry()->VerifyReplay({}, sequence_number);
        return ZX_OK;
      },
  };
  MockTransactionHandler handler(registry(), callbacks, std::size(callbacks));
  {
    Journal journal(&handler, take_info(), take_journal_buffer(), take_data_buffer(),
                    kJournalStartBlock, Journal::Options());
    journal.schedule_task(journal.WriteMetadata({operations[0]}));
    journal.schedule_task(journal.WriteMetadata({operations[1]}));
  }
}
TEST_F ( JournalTest , WriteToOverfilledJournalUpdatesInfoBlock ) { storage :: VmoBuffer metadata = registry ( ) -> InitializeBuffer ( kJournalLength ) ; const std :: vector < storage :: UnbufferedOperation > operations = { { zx :: unowned_vmo ( metadata . vmo ( ) . get ( ) ) , { storage :: OperationType :: kWrite , . vmo_offset = 0 , . dev_offset = 20 , . length = 6 , } , } , { zx :: unowned_vmo ( metadata . vmo ( ) . get ( ) ) , { storage :: OperationType :: kWrite , . vmo_offset = 2 , . dev_offset = 1234 , . length = 1 , } , } , } ; ASSERT_EQ ( kJournalLength + 1 , 2 * kEntryMetadataBlocks + operations [ 0 ] . op . length + operations [ 1 ] . op . length , " " ) ; constexpr uint64_t kJournalStartBlock = 55 ; JournalRequestVerifier verifier ( registry ( ) -> info ( ) , registry ( ) -> journal ( ) , registry ( ) -> writeback ( ) , kJournalStartBlock ) ; MockTransactionHandler :: TransactionCallback callbacks [ ] = { [ & ] ( const std :: vector < storage :: BufferedOperation > & requests ) { verifier . VerifyJournalWrite ( operations [ 0 ] , requests ) ; return ZX_OK ; } , [ & ] ( const std :: vector < storage :: BufferedOperation > & requests ) { verifier . VerifyMetadataWrite ( operations [ 0 ] , requests ) ; verifier . ExtendJournalOffset ( operations [ 0 ] . op . length + kEntryMetadataBlocks ) ; return ZX_OK ; } , [ & ] ( const std :: vector < storage :: BufferedOperation > & requests ) { uint64_t sequence_number = 1 ; verifier . VerifyInfoBlockWrite ( sequence_number , requests ) ; registry ( ) -> VerifyReplay ( { } , sequence_number ) ; return ZX_OK ; } , [ & ] ( const std :: vector < storage :: BufferedOperation > & requests ) { verifier . VerifyJournalWrite ( operations [ 1 ] , requests ) ; return ZX_OK ; } , [ & ] ( const std :: vector < storage :: BufferedOperation > & requests ) { verifier . VerifyMetadataWrite ( operations [ 1 ] , requests ) ; verifier . ExtendJournalOffset ( operations [ 1 ] . op . length + kEntryMetadataBlocks ) ; uint64_t sequence_number = 2 ; registry ( ) -> VerifyReplay ( { operations [ 1 ] } , sequence_number ) ; return ZX_OK ; } , [ & ] ( const std :: vector < storage :: BufferedOperation > & requests ) { uint64_t sequence_number = 2 ; verifier . VerifyInfoBlockWrite ( sequence_number , requests ) ; registry ( ) -> VerifyReplay ( { } , sequence_number ) ; return ZX_OK ; } , } ; MockTransactionHandler handler ( registry ( ) , callbacks , std :: size ( callbacks ) ) ; { Journal journal ( & handler , take_info ( ) , take_journal_buffer ( ) , take_data_buffer ( ) , kJournalStartBlock , Journal :: Options ( ) ) ; journal . schedule_task ( journal . WriteMetadata ( { operations [ 0 ] } ) ) ; journal . schedule_task ( journal . WriteMetadata ( { operations [ 1 ] } ) ) ; } }
Tests that the info block is updated if a metadata write would invalidate
the entry pointed to by "start block".
Tests that the info block is updated if a metadata write would invalidate the entry pointed to by " start block " .
// Before we update the info block, check that a power failure would result in // only the second metadata operation being replayed. // // The first operation has already completed and peristed thanks to the earlier // info block update. // After we update the info block, check that a power failure would result in // no operations being replayed - this equivalent to the "clean shutdown" case, // where there should be no work to do on reboot.