[incfs] Space trimming for IncFS v1
Run a manual timed job that trims all files one by one on the old version of IncFS, where it didn't do it automatically. Bug: 183435580 Fixes: 183436717 Test: atest libincfs-test service.incremental_test Change-Id: I57885b2826e383814822c767802f837135fd8464
This commit is contained in:
committed by
Alex Buynytskyy
parent
16828074ec
commit
4cd2492576
@@ -1086,17 +1086,14 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m
|
||||
return err;
|
||||
}
|
||||
if (params.size > 0) {
|
||||
// Only v2+ incfs supports automatically trimming file over-reserved sizes
|
||||
if (mIncFs->features() & incfs::Features::v2) {
|
||||
if (auto err = mIncFs->reserveSpace(ifs->control, normPath, params.size)) {
|
||||
if (err != -EOPNOTSUPP) {
|
||||
LOG(ERROR) << "Failed to reserve space for a new file: " << err;
|
||||
(void)mIncFs->unlink(ifs->control, normPath);
|
||||
return err;
|
||||
} else {
|
||||
LOG(WARNING) << "Reserving space for backing file isn't supported, "
|
||||
"may run out of disk later";
|
||||
}
|
||||
if (auto err = mIncFs->reserveSpace(ifs->control, id, params.size)) {
|
||||
if (err != -EOPNOTSUPP) {
|
||||
LOG(ERROR) << "Failed to reserve space for a new file: " << err;
|
||||
(void)mIncFs->unlink(ifs->control, normPath);
|
||||
return err;
|
||||
} else {
|
||||
LOG(WARNING) << "Reserving space for backing file isn't supported, "
|
||||
"may run out of disk later";
|
||||
}
|
||||
}
|
||||
if (!data.empty()) {
|
||||
@@ -1680,6 +1677,15 @@ void IncrementalService::runCmdLooper() {
|
||||
}
|
||||
}
|
||||
|
||||
void IncrementalService::trimReservedSpaceV1(const IncFsMount& ifs) {
|
||||
mIncFs->forEachFile(ifs.control, [this](auto&& control, auto&& fileId) {
|
||||
if (mIncFs->isFileFullyLoaded(control, fileId) == incfs::LoadingState::Full) {
|
||||
mIncFs->reserveSpace(control, fileId, -1);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void IncrementalService::prepareDataLoaderLocked(IncFsMount& ifs, DataLoaderParamsParcel&& params,
|
||||
DataLoaderStatusListener&& statusListener,
|
||||
const StorageHealthCheckParams& healthCheckParams,
|
||||
@@ -1699,6 +1705,22 @@ void IncrementalService::prepareDataLoaderLocked(IncFsMount& ifs, DataLoaderPara
|
||||
std::move(statusListener), healthCheckParams,
|
||||
std::move(healthListener), path::join(ifs.root, constants().mount));
|
||||
|
||||
// pre-v2 IncFS doesn't do automatic reserved space trimming - need to run it manually
|
||||
if (!(mIncFs->features() & incfs::Features::v2)) {
|
||||
addIfsStateCallback(ifs.mountId, [this](StorageId storageId, IfsState state) -> bool {
|
||||
if (!state.fullyLoaded) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto ifs = getIfs(storageId);
|
||||
if (!ifs) {
|
||||
return false;
|
||||
}
|
||||
trimReservedSpaceV1(*ifs);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
addIfsStateCallback(ifs.mountId, [this](StorageId storageId, IfsState state) -> bool {
|
||||
if (!state.fullyLoaded || state.readLogsEnabled) {
|
||||
return true;
|
||||
|
||||
@@ -452,6 +452,8 @@ private:
|
||||
StorageLoadingProgressListener&& progressListener);
|
||||
long getMillsSinceOldestPendingRead(StorageId storage);
|
||||
|
||||
void trimReservedSpaceV1(const IncFsMount& ifs);
|
||||
|
||||
private:
|
||||
const std::unique_ptr<VoldServiceWrapper> mVold;
|
||||
const std::unique_ptr<DataLoaderManagerWrapper> mDataLoaderManager;
|
||||
|
||||
@@ -212,6 +212,9 @@ public:
|
||||
std::string_view path) const final {
|
||||
return incfs::isFullyLoaded(control, path);
|
||||
}
|
||||
incfs::LoadingState isFileFullyLoaded(const Control& control, FileId id) const final {
|
||||
return incfs::isFullyLoaded(control, id);
|
||||
}
|
||||
incfs::LoadingState isEverythingFullyLoaded(const Control& control) const final {
|
||||
return incfs::isEverythingFullyLoaded(control);
|
||||
}
|
||||
@@ -227,9 +230,8 @@ public:
|
||||
ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const final {
|
||||
return incfs::writeBlocks({blocks.data(), size_t(blocks.size())});
|
||||
}
|
||||
ErrorCode reserveSpace(const Control& control, std::string_view path,
|
||||
IncFsSize size) const final {
|
||||
return incfs::reserveSpace(control, path, size);
|
||||
ErrorCode reserveSpace(const Control& control, FileId id, IncFsSize size) const final {
|
||||
return incfs::reserveSpace(control, id, size);
|
||||
}
|
||||
WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
|
||||
std::vector<incfs::ReadInfo>* pendingReadsBuffer) const final {
|
||||
@@ -238,19 +240,26 @@ public:
|
||||
ErrorCode setUidReadTimeouts(const Control& control,
|
||||
const std::vector<android::os::incremental::PerUidReadTimeouts>&
|
||||
perUidReadTimeouts) const final {
|
||||
std::vector<incfs::UidReadTimeouts> timeouts;
|
||||
timeouts.resize(perUidReadTimeouts.size());
|
||||
std::vector<incfs::UidReadTimeouts> timeouts(perUidReadTimeouts.size());
|
||||
for (int i = 0, size = perUidReadTimeouts.size(); i < size; ++i) {
|
||||
auto&& timeout = timeouts[i];
|
||||
auto& timeout = timeouts[i];
|
||||
const auto& perUidTimeout = perUidReadTimeouts[i];
|
||||
timeout.uid = perUidTimeout.uid;
|
||||
timeout.minTimeUs = perUidTimeout.minTimeUs;
|
||||
timeout.minPendingTimeUs = perUidTimeout.minPendingTimeUs;
|
||||
timeout.maxPendingTimeUs = perUidTimeout.maxPendingTimeUs;
|
||||
}
|
||||
|
||||
return incfs::setUidReadTimeouts(control, timeouts);
|
||||
}
|
||||
ErrorCode forEachFile(const Control& control, FileCallback cb) const final {
|
||||
return incfs::forEachFile(control,
|
||||
[&](auto& control, FileId id) { return cb(control, id); });
|
||||
}
|
||||
ErrorCode forEachIncompleteFile(const Control& control, FileCallback cb) const final {
|
||||
return incfs::forEachIncompleteFile(control, [&](auto& control, FileId id) {
|
||||
return cb(control, id);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
static JNIEnv* getOrAttachJniEnv(JavaVM* jvm);
|
||||
|
||||
@@ -84,6 +84,8 @@ public:
|
||||
void(std::string_view root, std::string_view backingDir,
|
||||
std::span<std::pair<std::string_view, std::string_view>> binds)>;
|
||||
|
||||
using FileCallback = android::base::function_ref<bool(const Control& control, FileId fileId)>;
|
||||
|
||||
static std::string toString(FileId fileId);
|
||||
|
||||
virtual ~IncFsWrapper() = default;
|
||||
@@ -105,14 +107,14 @@ public:
|
||||
const Control& control, std::string_view path) const = 0;
|
||||
virtual incfs::LoadingState isFileFullyLoaded(const Control& control,
|
||||
std::string_view path) const = 0;
|
||||
virtual incfs::LoadingState isFileFullyLoaded(const Control& control, FileId id) const = 0;
|
||||
virtual incfs::LoadingState isEverythingFullyLoaded(const Control& control) const = 0;
|
||||
virtual ErrorCode link(const Control& control, std::string_view from,
|
||||
std::string_view to) const = 0;
|
||||
virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0;
|
||||
virtual UniqueFd openForSpecialOps(const Control& control, FileId id) const = 0;
|
||||
virtual ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const = 0;
|
||||
virtual ErrorCode reserveSpace(const Control& control, std::string_view path,
|
||||
IncFsSize size) const = 0;
|
||||
virtual ErrorCode reserveSpace(const Control& control, FileId id, IncFsSize size) const = 0;
|
||||
virtual WaitResult waitForPendingReads(
|
||||
const Control& control, std::chrono::milliseconds timeout,
|
||||
std::vector<incfs::ReadInfo>* pendingReadsBuffer) const = 0;
|
||||
@@ -120,6 +122,8 @@ public:
|
||||
const Control& control,
|
||||
const std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts)
|
||||
const = 0;
|
||||
virtual ErrorCode forEachFile(const Control& control, FileCallback cb) const = 0;
|
||||
virtual ErrorCode forEachIncompleteFile(const Control& control, FileCallback cb) const = 0;
|
||||
};
|
||||
|
||||
class AppOpsManagerWrapper {
|
||||
|
||||
@@ -379,6 +379,7 @@ public:
|
||||
std::string_view path));
|
||||
MOCK_CONST_METHOD2(isFileFullyLoaded,
|
||||
incfs::LoadingState(const Control& control, std::string_view path));
|
||||
MOCK_CONST_METHOD2(isFileFullyLoaded, incfs::LoadingState(const Control& control, FileId id));
|
||||
MOCK_CONST_METHOD1(isEverythingFullyLoaded, incfs::LoadingState(const Control& control));
|
||||
MOCK_CONST_METHOD3(link,
|
||||
ErrorCode(const Control& control, std::string_view from,
|
||||
@@ -386,14 +387,15 @@ public:
|
||||
MOCK_CONST_METHOD2(unlink, ErrorCode(const Control& control, std::string_view path));
|
||||
MOCK_CONST_METHOD2(openForSpecialOps, UniqueFd(const Control& control, FileId id));
|
||||
MOCK_CONST_METHOD1(writeBlocks, ErrorCode(std::span<const DataBlock> blocks));
|
||||
MOCK_CONST_METHOD3(reserveSpace,
|
||||
ErrorCode(const Control& control, std::string_view path, IncFsSize size));
|
||||
MOCK_CONST_METHOD3(reserveSpace, ErrorCode(const Control& control, FileId id, IncFsSize size));
|
||||
MOCK_CONST_METHOD3(waitForPendingReads,
|
||||
WaitResult(const Control& control, std::chrono::milliseconds timeout,
|
||||
std::vector<incfs::ReadInfo>* pendingReadsBuffer));
|
||||
MOCK_CONST_METHOD2(setUidReadTimeouts,
|
||||
ErrorCode(const Control& control,
|
||||
const std::vector<PerUidReadTimeouts>& perUidReadTimeouts));
|
||||
MOCK_CONST_METHOD2(forEachFile, ErrorCode(const Control& control, FileCallback cb));
|
||||
MOCK_CONST_METHOD2(forEachIncompleteFile, ErrorCode(const Control& control, FileCallback cb));
|
||||
|
||||
MockIncFs() {
|
||||
ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return());
|
||||
@@ -1594,7 +1596,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedNoData) {
|
||||
int storageId =
|
||||
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
|
||||
IncrementalService::CreateOptions::CreateNew);
|
||||
EXPECT_CALL(*mIncFs, isFileFullyLoaded(_, _))
|
||||
EXPECT_CALL(*mIncFs, isFileFullyLoaded(_, An<std::string_view>()))
|
||||
.Times(1)
|
||||
.WillOnce(Return(incfs::LoadingState::MissingBlocks));
|
||||
ASSERT_GT((int)mIncrementalService->isFileFullyLoaded(storageId, "base.apk"), 0);
|
||||
@@ -1605,7 +1607,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedError) {
|
||||
int storageId =
|
||||
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
|
||||
IncrementalService::CreateOptions::CreateNew);
|
||||
EXPECT_CALL(*mIncFs, isFileFullyLoaded(_, _))
|
||||
EXPECT_CALL(*mIncFs, isFileFullyLoaded(_, An<std::string_view>()))
|
||||
.Times(1)
|
||||
.WillOnce(Return(incfs::LoadingState(-1)));
|
||||
ASSERT_LT((int)mIncrementalService->isFileFullyLoaded(storageId, "base.apk"), 0);
|
||||
@@ -1616,7 +1618,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccess) {
|
||||
int storageId =
|
||||
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
|
||||
IncrementalService::CreateOptions::CreateNew);
|
||||
EXPECT_CALL(*mIncFs, isFileFullyLoaded(_, _))
|
||||
EXPECT_CALL(*mIncFs, isFileFullyLoaded(_, An<std::string_view>()))
|
||||
.Times(1)
|
||||
.WillOnce(Return(incfs::LoadingState::Full));
|
||||
ASSERT_EQ(0, (int)mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
|
||||
|
||||
Reference in New Issue
Block a user