blob: 2697cb2c7415dbc0620c168da5626729ada79e0b [file] [log] [blame]
// Copyright 2017 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#pragma once
#include "android/base/Compiler.h"
#include "android/base/EnumFlags.h"
#include "android/base/Optional.h"
#include "android/base/files/StdioStream.h"
#include "android/base/synchronization/MessageChannel.h"
#include "android/base/system/System.h"
#include "android/base/threads/FunctorThread.h"
#include "android/base/threads/ThreadPool.h"
#include "android/snapshot/GapTracker.h"
#include "android/snapshot/MemoryWatch.h"
#include "android/snapshot/common.h"
#include <array>
#include <atomic>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
namespace android {
namespace snapshot {
using namespace ::android::base::EnumFlags;
class RamLoader {
DISALLOW_COPY_AND_ASSIGN(RamLoader);
public:
enum class Flags : uint8_t {
None = 0x0,
LoadIndexOnly = 0x1,
OnDemandAllowed = 0x2,
};
enum class State : uint8_t { Empty, Reading, Read, Filling, Filled, Error };
struct Page;
using Pages = std::vector<Page>;
struct FileIndex {
struct Block {
RamBlock ramBlock;
Pages::iterator pagesBegin;
Pages::iterator pagesEnd;
};
using Blocks = std::vector<Block>;
IndexFlags flags;
Blocks blocks;
Pages pages;
void clear();
};
struct RamBlockStructure {
~RamBlockStructure();
uint64_t pageSize;
std::vector<RamBlock> blocks;
};
RamLoader(base::StdioStream&& stream,
Flags flags,
const RamBlockStructure& blockStructure = {});
~RamLoader();
RamBlockStructure getRamBlockStructure() const;
void applyRamBlockStructure(const RamBlockStructure& blockStructure);
void clearIndex();
void loadRam(void* ptr, uint64_t size);
void registerBlock(const RamBlock& block);
bool start(bool isQuickboot);
bool wasStarted() const { return mWasStarted; }
void join();
void interrupt();
void touchAllPages();
bool hasError() const { return mHasError; }
void invalidateGaps() { mGaps.reset(nullptr); }
bool hasGaps() const { return mGaps ? 1 : 0; }
bool onDemandEnabled() const { return mOnDemandEnabled; }
bool onDemandLoadingComplete() const {
return mLoadingCompleted.load(std::memory_order_relaxed);
}
bool compressed() const {
return (mIndex.flags & IndexFlags::CompressedPages) != 0;
}
uint64_t diskSize() const { return mDiskSize; }
int version() const { return mVersion; }
uint64_t indexOffset() const { return mIndexPos; }
const Page* findPage(int blockIndex, const char* id, int pageIndex) const;
void acquireGapTracker(GapTracker::Ptr gaps) { mGaps = std::move(gaps); }
GapTracker::Ptr releaseGapTracker() { return std::move(mGaps); }
bool getDuration(base::System::Duration* duration) {
if (mEndTime < mStartTime) {
return false;
}
if (duration) {
*duration = mEndTime - mStartTime;
}
return true;
}
bool didSwitchFileBacking() const {
return mLoadedFromFileBacking || mLoadedToFileBacking;
}
private:
bool readIndex();
void readBlockPages(base::Stream* stream,
FileIndex::Blocks::iterator blockIt,
bool compressed,
int64_t* runningFilePos,
int32_t* prevPageSizeOnDisk);
bool registerPageWatches();
void zeroOutPage(const Page& page);
uint8_t* pagePtr(const Page& page) const;
uint32_t pageSize(const Page& page) const;
Page& page(void* ptr);
void loadRamPage(void* ptr);
bool readDataFromDisk(Page* pagePtr, uint8_t* preallocatedBuffer = nullptr);
void fillPageData(Page* pagePtr);
void readerWorker();
MemoryAccessWatch::IdleCallbackResult backgroundPageLoad();
MemoryAccessWatch::IdleCallbackResult fillPageInBackground(Page* page);
void interruptReading();
bool readAllPages();
void startDecompressor();
base::StdioStream mStream;
int mStreamFd; // An FD for the |mStream|'s underlying open file.
bool mWasStarted = false;
std::atomic<bool> mHasError{false};
base::Optional<MemoryAccessWatch> mAccessWatch;
base::FunctorThread mReaderThread;
Pages::iterator mBackgroundPageIt;
bool mSentEndOfPagesMarker = false;
bool mJoining = false;
bool mOnDemandEnabled = false;
base::MessageChannel<Page*, 32> mReadingQueue;
base::MessageChannel<Page*, 32> mReadDataQueue;
base::Optional<base::ThreadPool<Page*>> mDecompressor;
FileIndex mIndex;
GapTracker::Ptr mGaps;
uint64_t mDiskSize = 0;
uint64_t mIndexPos = 0;
int mVersion = 0;
base::System::Duration mStartTime = 0;
base::System::Duration mEndTime = 0;
// Assumed to be a power of 2 for convenient
// rounding and aligning
uint64_t mPageSize = kDefaultPageSize;
// Flag to stop lazy RAM loading if loading has
// completed.
std::atomic<bool> mLoadingCompleted{false};
// Whether or not this ram load is part of
// quickboot load.
bool mIsQuickboot = false;
// Whether or not we just want to reload the index.
bool mIndexOnly = false;
// Whether we loaded eagerly from a ram.img
bool mLoadedFromFileBacking = false;
// Whether we loaded to a ram.img
bool mLoadedToFileBacking = false;
// Whether we are currently lazy loading from a ram.img by mmap
bool mLazyLoadingFromFileBacking = false;
};
struct RamLoader::Page {
std::atomic<uint8_t> state{uint8_t(State::Empty)};
uint16_t blockIndex;
uint32_t sizeOnDisk;
uint64_t filePos;
std::array<char, 16> hash;
uint8_t* data;
Page() = default;
Page(RamLoader::State state) : state(uint8_t(state)) {}
Page(Page&& other)
: state(other.state.load(std::memory_order_relaxed)),
blockIndex(other.blockIndex),
sizeOnDisk(other.sizeOnDisk),
filePos(other.filePos),
data(other.data) {}
Page& operator=(Page&& other) {
state.store(other.state.load(std::memory_order_relaxed),
std::memory_order_relaxed);
blockIndex = other.blockIndex;
sizeOnDisk = other.sizeOnDisk;
filePos = other.filePos;
data = other.data;
return *this;
}
bool zeroed() const { return sizeOnDisk == 0; }
};
} // namespace snapshot
} // namespace android