diff options
author | alk3pInjection <webmaster@raspii.tech> | 2022-05-02 09:56:20 +0800 |
---|---|---|
committer | alk3pInjection <webmaster@raspii.tech> | 2022-05-02 09:56:20 +0800 |
commit | d53ccefe1270307c78ac8f833c82abcaa262cc3a (patch) | |
tree | 6993ccbaec8f775df73b75d38c5b529889479417 | |
parent | adbb1c10dd503804988344c1905df3c23973cf0d (diff) | |
parent | 8fc3f7296741ce589f7294113f8771837fb49bda (diff) |
Merge tag 'LA.QSSI.12.0.r1-06800-qssi.0' into sugisawa-mr1
"LA.QSSI.12.0.r1-06800-qssi.0"
Change-Id: I979d0f844cf44539ca87aade5929a4c84b4d31ff
23 files changed, 466 insertions, 129 deletions
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp index 8314ec596..541f254cb 100644 --- a/fs_mgr/libdm/dm_test.cpp +++ b/fs_mgr/libdm/dm_test.cpp @@ -684,13 +684,9 @@ TEST(libdm, DeleteDeviceDeferredWaitsForLastReference) { TEST(libdm, CreateEmptyDevice) { DeviceMapper& dm = DeviceMapper::Instance(); ASSERT_TRUE(dm.CreateEmptyDevice("empty-device")); - auto guard = android::base::make_scope_guard([&]() { dm.DeleteDevice("empty-device", 5s); }); + auto guard = + android::base::make_scope_guard([&]() { dm.DeleteDeviceIfExists("empty-device", 5s); }); // Empty device should be in suspended state. ASSERT_EQ(DmDeviceState::SUSPENDED, dm.GetState("empty-device")); - - std::string path; - ASSERT_TRUE(dm.WaitForDevice("empty-device", 5s, &path)); - // Path should exist. - ASSERT_EQ(0, access(path.c_str(), F_OK)); } diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index 9bf5db18e..69d89676b 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -386,6 +386,17 @@ class SnapshotManager final : public ISnapshotManager { // first-stage to decide whether to launch snapuserd. bool IsSnapuserdRequired(); + // Add new public entries above this line. + + // Helpers for failure injection. + using MergeConsistencyChecker = + std::function<MergeFailureCode(const std::string& name, const SnapshotStatus& status)>; + + void set_merge_consistency_checker(MergeConsistencyChecker checker) { + merge_consistency_checker_ = checker; + } + MergeConsistencyChecker merge_consistency_checker() const { return merge_consistency_checker_; } + private: FRIEND_TEST(SnapshotTest, CleanFirstStageMount); FRIEND_TEST(SnapshotTest, CreateSnapshot); @@ -400,6 +411,7 @@ class SnapshotManager final : public ISnapshotManager { FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot); FRIEND_TEST(SnapshotTest, UpdateBootControlHal); FRIEND_TEST(SnapshotUpdateTest, AddPartition); + FRIEND_TEST(SnapshotUpdateTest, ConsistencyCheckResume); FRIEND_TEST(SnapshotUpdateTest, DaemonTransition); FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback); FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery); @@ -782,6 +794,7 @@ class SnapshotManager final : public ISnapshotManager { std::function<bool(const std::string&)> uevent_regen_callback_; std::unique_ptr<SnapuserdClient> snapuserd_client_; std::unique_ptr<LpMetadata> old_partition_metadata_; + MergeConsistencyChecker merge_consistency_checker_; }; } // namespace snapshot diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index 4c94da28f..40cb35be1 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -87,6 +87,8 @@ static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot"; static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator"; static constexpr auto kUpdateStateCheckInterval = 2s; +MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status); + // Note: IImageManager is an incomplete type in the header, so the default // destructor doesn't work. SnapshotManager::~SnapshotManager() {} @@ -116,6 +118,7 @@ std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceI SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) { metadata_dir_ = device_->GetMetadataDir(); + merge_consistency_checker_ = android::snapshot::CheckMergeConsistency; } static std::string GetCowName(const std::string& snapshot_name) { @@ -1175,6 +1178,10 @@ MergeFailureCode SnapshotManager::CheckMergeConsistency(LockedFile* lock, const const SnapshotStatus& status) { CHECK(lock); + return merge_consistency_checker_(name, status); +} + +MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status) { if (!status.compression_enabled()) { // Do not try to verify old-style COWs yet. return MergeFailureCode::Ok; @@ -1252,9 +1259,11 @@ MergeFailureCode SnapshotManager::MergeSecondPhaseSnapshots(LockedFile* lock) { } SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock); - CHECK(update_status.state() == UpdateState::Merging); + CHECK(update_status.state() == UpdateState::Merging || + update_status.state() == UpdateState::MergeFailed); CHECK(update_status.merge_phase() == MergePhase::FIRST_PHASE); + update_status.set_state(UpdateState::Merging); update_status.set_merge_phase(MergePhase::SECOND_PHASE); if (!WriteSnapshotUpdateStatus(lock, update_status)) { return MergeFailureCode::WriteStatus; @@ -2556,6 +2565,7 @@ bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state, SnapshotUpdateStatus old_status = ReadSnapshotUpdateStatus(lock); status.set_compression_enabled(old_status.compression_enabled()); status.set_source_build_fingerprint(old_status.source_build_fingerprint()); + status.set_merge_phase(old_status.merge_phase()); } return WriteSnapshotUpdateStatus(lock, status); } diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index 7630efe3f..3a3aedc57 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp @@ -1297,6 +1297,92 @@ TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) { } } +// Test that a transient merge consistency check failure can resume properly. +TEST_F(SnapshotUpdateTest, ConsistencyCheckResume) { + if (!IsCompressionEnabled()) { + // b/179111359 + GTEST_SKIP() << "Skipping Virtual A/B Compression test"; + } + + auto old_sys_size = GetSize(sys_); + auto old_prd_size = GetSize(prd_); + + // Grow |sys| but shrink |prd|. + SetSize(sys_, old_sys_size * 2); + sys_->set_estimate_cow_size(8_MiB); + SetSize(prd_, old_prd_size / 2); + prd_->set_estimate_cow_size(1_MiB); + + AddOperationForPartitions(); + + ASSERT_TRUE(sm->BeginUpdate()); + ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_)); + ASSERT_TRUE(WriteSnapshotAndHash("sys_b")); + ASSERT_TRUE(WriteSnapshotAndHash("vnd_b")); + ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size)); + + sync(); + + // Assert that source partitions aren't affected. + for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) { + ASSERT_TRUE(IsPartitionUnchanged(name)); + } + + ASSERT_TRUE(sm->FinishedSnapshotWrites(false)); + + // Simulate shutting down the device. + ASSERT_TRUE(UnmapAll()); + + // After reboot, init does first stage mount. + auto init = NewManagerForFirstStageMount("_b"); + ASSERT_NE(init, nullptr); + ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); + ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); + + // Check that the target partitions have the same content. + for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { + ASSERT_TRUE(IsPartitionUnchanged(name)); + } + + auto old_checker = init->merge_consistency_checker(); + + init->set_merge_consistency_checker( + [](const std::string&, const SnapshotStatus&) -> MergeFailureCode { + return MergeFailureCode::WrongMergeCountConsistencyCheck; + }); + + // Initiate the merge and wait for it to be completed. + ASSERT_TRUE(init->InitiateMerge()); + { + // Check that the merge phase is FIRST_PHASE until at least one call + // to ProcessUpdateState() occurs. + ASSERT_TRUE(AcquireLock()); + auto local_lock = std::move(lock_); + auto status = init->ReadSnapshotUpdateStatus(local_lock.get()); + ASSERT_EQ(status.merge_phase(), MergePhase::FIRST_PHASE); + } + + // Merge should have failed. + ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState()); + + // Simulate shutting down the device and creating partitions again. + ASSERT_TRUE(UnmapAll()); + + // Restore the checker. + init->set_merge_consistency_checker(std::move(old_checker)); + + ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); + + // Complete the merge. + ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState()); + + // Check that the target partitions have the same content after the merge. + for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { + ASSERT_TRUE(IsPartitionUnchanged(name)) + << "Content of " << name << " changes after the merge"; + } +} + // Test that if new system partitions uses empty space in super, that region is not snapshotted. TEST_F(SnapshotUpdateTest, DirectWriteEmptySpace) { GTEST_SKIP() << "b/141889746"; diff --git a/fs_mgr/libsnapshot/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd_readahead.cpp index 16d5919a5..e55fea382 100644 --- a/fs_mgr/libsnapshot/snapuserd_readahead.cpp +++ b/fs_mgr/libsnapshot/snapuserd_readahead.cpp @@ -226,9 +226,15 @@ bool ReadAheadThread::ReconstructDataFromCow() { int num_ops = 0; int total_blocks_merged = 0; + // This memcpy is important as metadata_buffer_ will be an unaligned address and will fault + // on 32-bit systems + std::unique_ptr<uint8_t[]> metadata_buffer = + std::make_unique<uint8_t[]>(snapuserd_->GetBufferMetadataSize()); + memcpy(metadata_buffer.get(), metadata_buffer_, snapuserd_->GetBufferMetadataSize()); + while (true) { struct ScratchMetadata* bm = reinterpret_cast<struct ScratchMetadata*>( - (char*)metadata_buffer_ + metadata_offset); + (char*)metadata_buffer.get() + metadata_offset); // Done reading metadata if (bm->new_block == 0 && bm->file_offset == 0) { diff --git a/init/Android.bp b/init/Android.bp index 69f8815ed..00ac6a752 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -97,7 +97,19 @@ cc_library_static { ], } -cc_defaults { +soong_config_module_type { + name: "libinit_cc_defaults", + module_type: "cc_defaults", + config_namespace: "ANDROID", + bool_variables: [ + "PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT", + ], + properties: [ + "cflags", + ], +} + +libinit_cc_defaults { name: "init_defaults", sanitize: { misc_undefined: ["signed-integer-overflow"], @@ -117,6 +129,7 @@ cc_defaults { "-DDUMP_ON_UMOUNT_FAILURE=0", "-DSHUTDOWN_ZERO_TIMEOUT=0", "-DINIT_FULL_SOURCES", + "-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=0", ], product_variables: { debuggable: { @@ -145,6 +158,14 @@ cc_defaults { cppflags: ["-DUSER_MODE_LINUX"], }, }, + soong_config_variables: { + PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT: { + cflags: [ + "-UINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT", + "-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=1", + ], + }, + }, static_libs: [ "libavb", "libc++fs", diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp index a86e0733f..1bd91f7b4 100644 --- a/init/first_stage_init.cpp +++ b/init/first_stage_init.cpp @@ -330,14 +330,21 @@ int FirstStageMain(int argc, char** argv) { // If "/force_debuggable" is present, the second-stage init will use a userdebug // sepolicy and load adb_debug.prop to allow adb root, if the device is unlocked. if (access("/force_debuggable", F_OK) == 0) { + constexpr const char adb_debug_prop_src[] = "/adb_debug.prop"; + constexpr const char userdebug_plat_sepolicy_cil_src[] = "/userdebug_plat_sepolicy.cil"; std::error_code ec; // to invoke the overloaded copy_file() that won't throw. - if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) || - !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) { - LOG(ERROR) << "Failed to setup debug ramdisk"; - } else { - // setenv for second-stage init to read above kDebugRamdisk* files. - setenv("INIT_FORCE_DEBUGGABLE", "true", 1); + if (access(adb_debug_prop_src, F_OK) == 0 && + !fs::copy_file(adb_debug_prop_src, kDebugRamdiskProp, ec)) { + LOG(WARNING) << "Can't copy " << adb_debug_prop_src << " to " << kDebugRamdiskProp + << ": " << ec.message(); + } + if (access(userdebug_plat_sepolicy_cil_src, F_OK) == 0 && + !fs::copy_file(userdebug_plat_sepolicy_cil_src, kDebugRamdiskSEPolicy, ec)) { + LOG(WARNING) << "Can't copy " << userdebug_plat_sepolicy_cil_src << " to " + << kDebugRamdiskSEPolicy << ": " << ec.message(); } + // setenv for second-stage init to read above kDebugRamdisk* files. + setenv("INIT_FORCE_DEBUGGABLE", "true", 1); } if (ForceNormalBoot(cmdline, bootconfig)) { diff --git a/init/selinux.cpp b/init/selinux.cpp index 42d302324..29c0ff3ba 100644 --- a/init/selinux.cpp +++ b/init/selinux.cpp @@ -295,6 +295,25 @@ bool IsSplitPolicyDevice() { return access(plat_policy_cil_file, R_OK) != -1; } +std::optional<const char*> GetUserdebugPlatformPolicyFile() { + // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil. + const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE"); + if (force_debuggable_env && "true"s == force_debuggable_env && AvbHandle::IsDeviceUnlocked()) { + const std::vector<const char*> debug_policy_candidates = { +#if INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT == 1 + "/system_ext/etc/selinux/userdebug_plat_sepolicy.cil", +#endif + kDebugRamdiskSEPolicy, + }; + for (const char* debug_policy : debug_policy_candidates) { + if (access(debug_policy, F_OK) == 0) { + return debug_policy; + } + } + } + return std::nullopt; +} + struct PolicyFile { unique_fd fd; std::string path; @@ -310,13 +329,10 @@ bool OpenSplitPolicy(PolicyFile* policy_file) { // secilc is invoked to compile the above three policy files into a single monolithic policy // file. This file is then loaded into the kernel. - // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil. - const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE"); - bool use_userdebug_policy = - ((force_debuggable_env && "true"s == force_debuggable_env) && - AvbHandle::IsDeviceUnlocked() && access(kDebugRamdiskSEPolicy, F_OK) == 0); + const auto userdebug_plat_sepolicy = GetUserdebugPlatformPolicyFile(); + const bool use_userdebug_policy = userdebug_plat_sepolicy.has_value(); if (use_userdebug_policy) { - LOG(WARNING) << "Using userdebug system sepolicy"; + LOG(INFO) << "Using userdebug system sepolicy " << *userdebug_plat_sepolicy; } // Load precompiled policy from vendor image, if a matching policy is found there. The policy @@ -413,7 +429,7 @@ bool OpenSplitPolicy(PolicyFile* policy_file) { // clang-format off std::vector<const char*> compile_args { "/system/bin/secilc", - use_userdebug_policy ? kDebugRamdiskSEPolicy: plat_policy_cil_file, + use_userdebug_policy ? *userdebug_plat_sepolicy : plat_policy_cil_file, "-m", "-M", "true", "-G", "-N", "-c", version_as_string.c_str(), plat_mapping_file.c_str(), diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp index 40467b7d3..7fd3f65ca 100644 --- a/init/snapuserd_transition.cpp +++ b/init/snapuserd_transition.cpp @@ -32,6 +32,7 @@ #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <cutils/sockets.h> +#include <fs_avb/fs_avb.h> #include <libsnapshot/snapshot.h> #include <libsnapshot/snapuserd_client.h> #include <private/android_filesystem_config.h> @@ -227,6 +228,56 @@ void SnapuserdSelinuxHelper::FinishTransition() { } } +/* + * Before starting init second stage, we will wait + * for snapuserd daemon to be up and running; bionic libc + * may read /system/etc/selinux/plat_property_contexts file + * before invoking main() function. This will happen if + * init initializes property during second stage. Any access + * to /system without snapuserd daemon will lead to a deadlock. + * + * Thus, we do a simple probe by reading system partition. This + * read will eventually be serviced by daemon confirming that + * daemon is up and running. Furthermore, we are still in the kernel + * domain and sepolicy has not been enforced yet. Thus, access + * to these device mapper block devices are ok even though + * we may see audit logs. + */ +bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() { + std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix(); + android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT)); + if (fd < 0) { + PLOG(ERROR) << "open " << dev << " failed"; + return false; + } + + void* addr; + ssize_t page_size = getpagesize(); + if (posix_memalign(&addr, page_size, page_size) < 0) { + PLOG(ERROR) << "posix_memalign with page size " << page_size; + return false; + } + + std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free); + + int iter = 0; + while (iter < 10) { + ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), buffer.get(), page_size, 0)); + if (n < 0) { + // Wait for sometime before retry + std::this_thread::sleep_for(100ms); + } else if (n == page_size) { + return true; + } else { + LOG(ERROR) << "pread returned: " << n << " from: " << dev << " expected: " << page_size; + } + + iter += 1; + } + + return false; +} + void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() { auto fd = GetRamdiskSnapuserdFd(); if (!fd) { @@ -248,6 +299,13 @@ void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() { setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1); LOG(INFO) << "Relaunched snapuserd with pid: " << pid; + + if (!TestSnapuserdIsReady()) { + PLOG(FATAL) << "snapuserd daemon failed to launch"; + } else { + LOG(INFO) << "snapuserd daemon is up and running"; + } + return; } diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h index a5ab652b7..757af1377 100644 --- a/init/snapuserd_transition.h +++ b/init/snapuserd_transition.h @@ -51,6 +51,7 @@ class SnapuserdSelinuxHelper final { private: void RelaunchFirstStageSnapuserd(); void ExecSnapuserd(); + bool TestSnapuserdIsReady(); std::unique_ptr<SnapshotManager> sm_; BlockDevInitializer block_dev_init_; diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h index 8f22d8983..ffd4d1285 100644 --- a/libcutils/include/private/android_filesystem_config.h +++ b/libcutils/include/private/android_filesystem_config.h @@ -157,6 +157,7 @@ #define AID_READPROC 3009 /* Allow /proc read access */ #define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */ #define AID_UHID 3011 /* Allow read/write to /dev/uhid node */ +#define AID_READTRACEFS 3012 /* Allow tracefs read */ /* The range 5000-5999 is also reserved for vendor partition. */ #define AID_OEM_RESERVED_2_START 5000 diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json index b133769a3..f513b7101 100644 --- a/libprocessgroup/profiles/task_profiles.json +++ b/libprocessgroup/profiles/task_profiles.json @@ -202,7 +202,19 @@ } ] }, - + { + "Name": "Dex2oatPerformance", + "Actions": [ + { + "Name": "JoinCgroup", + "Params": + { + "Controller": "cpu", + "Path": "dex2oat" + } + } + ] + }, { "Name": "CpuPolicySpread", "Actions": [ @@ -697,7 +709,7 @@ }, { "Name": "Dex2OatBootComplete", - "Profiles": [ "SCHED_SP_BACKGROUND" ] + "Profiles": [ "Dex2oatPerformance", "LowIoPriority", "TimerSlackHigh" ] } ] } diff --git a/libprocessgroup/profiles/task_profiles_28.json b/libprocessgroup/profiles/task_profiles_28.json index 56053e05a..e7be5487d 100644 --- a/libprocessgroup/profiles/task_profiles_28.json +++ b/libprocessgroup/profiles/task_profiles_28.json @@ -117,7 +117,19 @@ } ] }, - + { + "Name": "Dex2oatPerformance", + "Actions": [ + { + "Name": "JoinCgroup", + "Params": + { + "Controller": "schedtune", + "Path": "background" + } + } + ] + }, { "Name": "CpuPolicySpread", "Actions": [ diff --git a/libprocessgroup/profiles/task_profiles_29.json b/libprocessgroup/profiles/task_profiles_29.json index 52279b872..6174c8d0f 100644 --- a/libprocessgroup/profiles/task_profiles_29.json +++ b/libprocessgroup/profiles/task_profiles_29.json @@ -117,7 +117,19 @@ } ] }, - + { + "Name": "Dex2oatPerformance", + "Actions": [ + { + "Name": "JoinCgroup", + "Params": + { + "Controller": "schedtune", + "Path": "background" + } + } + ] + }, { "Name": "CpuPolicySpread", "Actions": [ diff --git a/libprocessgroup/profiles/task_profiles_30.json b/libprocessgroup/profiles/task_profiles_30.json index 56053e05a..e7be5487d 100644 --- a/libprocessgroup/profiles/task_profiles_30.json +++ b/libprocessgroup/profiles/task_profiles_30.json @@ -117,7 +117,19 @@ } ] }, - + { + "Name": "Dex2oatPerformance", + "Actions": [ + { + "Name": "JoinCgroup", + "Params": + { + "Controller": "schedtune", + "Path": "background" + } + } + ] + }, { "Name": "CpuPolicySpread", "Actions": [ diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp index 03d347973..f743ed81b 100644 --- a/libprocessgroup/sched_policy.cpp +++ b/libprocessgroup/sched_policy.cpp @@ -166,27 +166,7 @@ static int getCGroupSubsys(int tid, const char* subsys, std::string& subgroup) { return 0; } -int get_sched_policy(int tid, SchedPolicy* policy) { - if (tid == 0) { - tid = GetThreadId(); - } - - std::string group; - if (schedboost_enabled()) { - if ((getCGroupSubsys(tid, "schedtune", group) < 0) && - (getCGroupSubsys(tid, "cpu", group) < 0)) { - LOG(ERROR) << "Failed to find cpu cgroup for tid " << tid; - return -1; - } - } - if (group.empty() && cpusets_enabled()) { - if (getCGroupSubsys(tid, "cpuset", group) < 0) { - LOG(ERROR) << "Failed to find cpuset cgroup for tid " << tid; - return -1; - } - } - - // TODO: replace hardcoded directories +static int get_sched_policy_from_group(const std::string& group, SchedPolicy* policy) { if (group.empty()) { *policy = SP_FOREGROUND; } else if (group == "foreground") { @@ -208,6 +188,35 @@ int get_sched_policy(int tid, SchedPolicy* policy) { return 0; } +int get_sched_policy(int tid, SchedPolicy* policy) { + if (tid == 0) { + tid = GetThreadId(); + } + + std::string group; + if (schedboost_enabled()) { + if ((getCGroupSubsys(tid, "schedtune", group) < 0) && + (getCGroupSubsys(tid, "cpu", group) < 0)) { + LOG(ERROR) << "Failed to find cpu cgroup for tid " << tid; + return -1; + } + // Wipe invalid group to fallback to cpuset + if (!group.empty()) { + if (get_sched_policy_from_group(group, policy) < 0) { + group.clear(); + } else { + return 0; + } + } + } + + if (cpusets_enabled() && getCGroupSubsys(tid, "cpuset", group) < 0) { + LOG(ERROR) << "Failed to find cpuset cgroup for tid " << tid; + return -1; + } + return get_sched_policy_from_group(group, policy); +} + #else /* Stubs for non-Android targets. */ diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp index cf74e6557..3834f9194 100644 --- a/libprocessgroup/task_profiles.cpp +++ b/libprocessgroup/task_profiles.cpp @@ -144,30 +144,13 @@ bool SetAttributeAction::ExecuteForTask(int tid) const { return true; } -bool SetCgroupAction::IsAppDependentPath(const std::string& path) { - return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos; -} - -SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p) - : controller_(c), path_(p) { - // file descriptors for app-dependent paths can't be cached - if (IsAppDependentPath(path_)) { - // file descriptor is not cached - fd_.reset(FDS_APP_DEPENDENT); - return; - } - - // file descriptor can be cached later on request - fd_.reset(FDS_NOT_CACHED); -} - -void SetCgroupAction::EnableResourceCaching() { +void CachedFdProfileAction::EnableResourceCaching() { std::lock_guard<std::mutex> lock(fd_mutex_); if (fd_ != FDS_NOT_CACHED) { return; } - std::string tasks_path = controller_.GetTasksFilePath(path_); + std::string tasks_path = GetPath(); if (access(tasks_path.c_str(), W_OK) != 0) { // file is not accessible @@ -185,7 +168,7 @@ void SetCgroupAction::EnableResourceCaching() { fd_ = std::move(fd); } -void SetCgroupAction::DropResourceCaching() { +void CachedFdProfileAction::DropResourceCaching() { std::lock_guard<std::mutex> lock(fd_mutex_); if (fd_ == FDS_NOT_CACHED) { return; @@ -194,22 +177,59 @@ void SetCgroupAction::DropResourceCaching() { fd_.reset(FDS_NOT_CACHED); } -bool SetCgroupAction::AddTidToCgroup(int tid, int fd) { +bool CachedFdProfileAction::IsAppDependentPath(const std::string& path) { + return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos; +} + +void CachedFdProfileAction::InitFd(const std::string& path) { + // file descriptors for app-dependent paths can't be cached + if (IsAppDependentPath(path)) { + // file descriptor is not cached + fd_.reset(FDS_APP_DEPENDENT); + return; + } + // file descriptor can be cached later on request + fd_.reset(FDS_NOT_CACHED); +} + +SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p) + : controller_(c), path_(p) { + InitFd(controller_.GetTasksFilePath(path_)); +} + +bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) { if (tid <= 0) { return true; } std::string value = std::to_string(tid); - if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) < 0) { - // If the thread is in the process of exiting, don't flag an error - if (errno != ESRCH) { - PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd; - return false; + if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) == value.length()) { + return true; + } + + // If the thread is in the process of exiting, don't flag an error + if (errno == ESRCH) { + return true; + } + + // ENOSPC is returned when cpuset cgroup that we are joining has no online cpus + if (errno == ENOSPC && !strcmp(controller_name, "cpuset")) { + // This is an abnormal case happening only in testing, so report it only once + static bool empty_cpuset_reported = false; + + if (empty_cpuset_reported) { + return true; } + + LOG(ERROR) << "Failed to add task '" << value + << "' into cpuset because all cpus in that cpuset are offline"; + empty_cpuset_reported = true; + } else { + PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd; } - return true; + return false; } bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const { @@ -219,7 +239,7 @@ bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const { PLOG(WARNING) << "Failed to open " << procs_path; return false; } - if (!AddTidToCgroup(pid, tmp_fd)) { + if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) { LOG(ERROR) << "Failed to add task into cgroup"; return false; } @@ -231,7 +251,7 @@ bool SetCgroupAction::ExecuteForTask(int tid) const { std::lock_guard<std::mutex> lock(fd_mutex_); if (IsFdValid()) { // fd is cached, reuse it - if (!AddTidToCgroup(tid, fd_)) { + if (!AddTidToCgroup(tid, fd_, controller()->name())) { LOG(ERROR) << "Failed to add task into cgroup"; return false; } @@ -253,10 +273,10 @@ bool SetCgroupAction::ExecuteForTask(int tid) const { std::string tasks_path = controller()->GetTasksFilePath(path_); unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC))); if (tmp_fd < 0) { - PLOG(WARNING) << "Failed to open " << tasks_path << ": " << strerror(errno); + PLOG(WARNING) << "Failed to open " << tasks_path; return false; } - if (!AddTidToCgroup(tid, tmp_fd)) { + if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) { LOG(ERROR) << "Failed to add task into cgroup"; return false; } @@ -264,37 +284,73 @@ bool SetCgroupAction::ExecuteForTask(int tid) const { return true; } -bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const { - std::string filepath(filepath_), value(value_); +WriteFileAction::WriteFileAction(const std::string& path, const std::string& value, + bool logfailures) + : path_(path), value_(value), logfailures_(logfailures) { + InitFd(path_); +} - filepath = StringReplace(filepath, "<uid>", std::to_string(uid), true); - filepath = StringReplace(filepath, "<pid>", std::to_string(pid), true); - value = StringReplace(value, "<uid>", std::to_string(uid), true); - value = StringReplace(value, "<pid>", std::to_string(pid), true); +bool WriteFileAction::WriteValueToFile(const std::string& value, const std::string& path, + bool logfailures) { + // Use WriteStringToFd instead of WriteStringToFile because the latter will open file with + // O_TRUNC which causes kernfs_mutex contention + unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC))); - if (!WriteStringToFile(value, filepath)) { - if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath; + if (tmp_fd < 0) { + if (logfailures) PLOG(WARNING) << "Failed to open " << path; + return false; + } + + if (!WriteStringToFd(value, tmp_fd)) { + if (logfailures) PLOG(ERROR) << "Failed to write '" << value << "' to " << path; return false; } return true; } +bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const { + std::lock_guard<std::mutex> lock(fd_mutex_); + std::string value(value_); + std::string path(path_); + + value = StringReplace(value, "<uid>", std::to_string(uid), true); + value = StringReplace(value, "<pid>", std::to_string(pid), true); + path = StringReplace(path, "<uid>", std::to_string(uid), true); + path = StringReplace(path, "<pid>", std::to_string(pid), true); + + return WriteValueToFile(value, path, logfailures_); +} + bool WriteFileAction::ExecuteForTask(int tid) const { - std::string filepath(filepath_), value(value_); + std::lock_guard<std::mutex> lock(fd_mutex_); + std::string value(value_); int uid = getuid(); - filepath = StringReplace(filepath, "<uid>", std::to_string(uid), true); - filepath = StringReplace(filepath, "<pid>", std::to_string(tid), true); value = StringReplace(value, "<uid>", std::to_string(uid), true); value = StringReplace(value, "<pid>", std::to_string(tid), true); - if (!WriteStringToFile(value, filepath)) { - if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath; + if (IsFdValid()) { + // fd is cached, reuse it + if (!WriteStringToFd(value, fd_)) { + if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_; + return false; + } + return true; + } + + if (fd_ == FDS_INACCESSIBLE) { + // no permissions to access the file, ignore + return true; + } + + if (fd_ == FDS_APP_DEPENDENT) { + // application-dependent path can't be used with tid + PLOG(ERROR) << "Application profile can't be applied to a thread"; return false; } - return true; + return WriteValueToFile(value, path_, logfailures_); } bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const { diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h index 25a84b0c1..278892dd5 100644 --- a/libprocessgroup/task_profiles.h +++ b/libprocessgroup/task_profiles.h @@ -108,50 +108,67 @@ class SetAttributeAction : public ProfileAction { std::string value_; }; -// Set cgroup profile element -class SetCgroupAction : public ProfileAction { +// Abstract profile element for cached fd +class CachedFdProfileAction : public ProfileAction { public: - SetCgroupAction(const CgroupController& c, const std::string& p); - - virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const; - virtual bool ExecuteForTask(int tid) const; virtual void EnableResourceCaching(); virtual void DropResourceCaching(); - const CgroupController* controller() const { return &controller_; } - std::string path() const { return path_; } - - private: + protected: enum FdState { FDS_INACCESSIBLE = -1, FDS_APP_DEPENDENT = -2, FDS_NOT_CACHED = -3, }; - CgroupController controller_; - std::string path_; android::base::unique_fd fd_; mutable std::mutex fd_mutex_; static bool IsAppDependentPath(const std::string& path); - static bool AddTidToCgroup(int tid, int fd); + void InitFd(const std::string& path); bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; } + + virtual const std::string GetPath() const = 0; +}; + +// Set cgroup profile element +class SetCgroupAction : public CachedFdProfileAction { + public: + SetCgroupAction(const CgroupController& c, const std::string& p); + + virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const; + virtual bool ExecuteForTask(int tid) const; + + const CgroupController* controller() const { return &controller_; } + + protected: + const std::string GetPath() const override { return controller_.GetTasksFilePath(path_); } + + private: + CgroupController controller_; + std::string path_; + + static bool AddTidToCgroup(int tid, int fd, const char* controller_name); }; // Write to file action -class WriteFileAction : public ProfileAction { +class WriteFileAction : public CachedFdProfileAction { public: - WriteFileAction(const std::string& filepath, const std::string& value, - bool logfailures) noexcept - : filepath_(filepath), value_(value), logfailures_(logfailures) {} + WriteFileAction(const std::string& path, const std::string& value, bool logfailures); virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const; virtual bool ExecuteForTask(int tid) const; + protected: + const std::string GetPath() const override { return path_; } + private: - std::string filepath_, value_; + std::string path_, value_; bool logfailures_; + + static bool WriteValueToFile(const std::string& value, const std::string& path, + bool logfailures); }; class TaskProfile { diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp index 6e293c741..3bf577961 100644 --- a/libutils/Threads.cpp +++ b/libutils/Threads.cpp @@ -317,10 +317,7 @@ int androidSetThreadPriority(pid_t tid, int pri) if (pri >= ANDROID_PRIORITY_BACKGROUND) { rc = SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1; } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) { - SchedPolicy policy = SP_FOREGROUND; - // Change to the sched policy group of the process. - get_sched_policy(getpid(), &policy); - rc = SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true) ? 0 : -1; + rc = SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1; } if (rc) { diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp index c4c58eef3..42602e9ee 100644 --- a/llkd/libllkd.cpp +++ b/llkd/libllkd.cpp @@ -1283,8 +1283,7 @@ bool llkInit(const char* threadname) { llkEnableSysrqT &= !llkLowRam; if (debuggable) { llkEnableSysrqT |= llkCheckEng(LLK_ENABLE_SYSRQ_T_PROPERTY); - if (!LLK_ENABLE_DEFAULT) { // NB: default is currently true ... - llkEnable |= llkCheckEng(LLK_ENABLE_PROPERTY); + if (!LLK_ENABLE_DEFAULT) { khtEnable |= llkCheckEng(KHT_ENABLE_PROPERTY); } } diff --git a/llkd/llkd-debuggable.rc b/llkd/llkd-debuggable.rc index 4b11b1c7a..c07560910 100644 --- a/llkd/llkd-debuggable.rc +++ b/llkd/llkd-debuggable.rc @@ -1,5 +1,5 @@ on property:ro.debuggable=1 - setprop llk.enable ${ro.llk.enable:-1} + setprop llk.enable ${ro.llk.enable:-0} setprop khungtask.enable ${ro.khungtask.enable:-1} on property:ro.llk.enable=eng diff --git a/llkd/tests/llkd_test.cpp b/llkd/tests/llkd_test.cpp index 475512c38..8eb9b001f 100644 --- a/llkd/tests/llkd_test.cpp +++ b/llkd/tests/llkd_test.cpp @@ -69,13 +69,9 @@ void execute(const char* command) { seconds llkdSleepPeriod(char state) { auto default_eng = android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng"; auto default_enable = LLK_ENABLE_DEFAULT; - if (!LLK_ENABLE_DEFAULT && default_eng && - android::base::GetBoolProperty("ro.debuggable", false)) { - default_enable = true; - } default_enable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, default_enable); if (default_eng) { - GTEST_LOG_INFO << LLK_ENABLE_PROPERTY " defaults to \"eng\" thus " + GTEST_LOG_INFO << LLK_ENABLE_PROPERTY " defaults to " << (default_enable ? "true" : "false") << "\n"; } // Hail Mary hope is unconfigured. @@ -108,10 +104,6 @@ seconds llkdSleepPeriod(char state) { rest(); } default_enable = LLK_ENABLE_DEFAULT; - if (!LLK_ENABLE_DEFAULT && (android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng") && - android::base::GetBoolProperty("ro.debuggable", false)) { - default_enable = true; - } default_enable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, default_enable); if (default_enable) { execute("start llkd-1"); diff --git a/rootdir/init.rc b/rootdir/init.rc index 8272654c7..e0f19acc0 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -82,8 +82,8 @@ on early-init mkdir /dev/boringssl 0755 root root mkdir /dev/boringssl/selftest 0755 root root - # Mount tracefs - mount tracefs tracefs /sys/kernel/tracing + # Mount tracefs (with GID=AID_READTRACEFS) + mount tracefs tracefs /sys/kernel/tracing gid=3012 # create sys dirctory mkdir /dev/sys 0755 system system @@ -163,6 +163,7 @@ on init mkdir /dev/cpuctl/rt mkdir /dev/cpuctl/system mkdir /dev/cpuctl/system-background + mkdir /dev/cpuctl/dex2oat chown system system /dev/cpuctl chown system system /dev/cpuctl/foreground chown system system /dev/cpuctl/background @@ -170,6 +171,7 @@ on init chown system system /dev/cpuctl/rt chown system system /dev/cpuctl/system chown system system /dev/cpuctl/system-background + chown system system /dev/cpuctl/dex2oat chown system system /dev/cpuctl/tasks chown system system /dev/cpuctl/foreground/tasks chown system system /dev/cpuctl/background/tasks @@ -177,6 +179,7 @@ on init chown system system /dev/cpuctl/rt/tasks chown system system /dev/cpuctl/system/tasks chown system system /dev/cpuctl/system-background/tasks + chown system system /dev/cpuctl/dex2oat/tasks chmod 0664 /dev/cpuctl/tasks chmod 0664 /dev/cpuctl/foreground/tasks chmod 0664 /dev/cpuctl/background/tasks @@ -184,6 +187,7 @@ on init chmod 0664 /dev/cpuctl/rt/tasks chmod 0664 /dev/cpuctl/system/tasks chmod 0664 /dev/cpuctl/system-background/tasks + chmod 0664 /dev/cpuctl/dex2oat/tasks # Create a cpu group for NNAPI HAL processes mkdir /dev/cpuctl/nnapi-hal |