diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp index 19445c8c5..70a1045a5 100644 --- a/fs_mgr/fs_mgr.cpp +++ b/fs_mgr/fs_mgr.cpp @@ -79,6 +79,7 @@ #define ZRAM_CONF_DEV "/sys/block/zram0/disksize" #define ZRAM_CONF_MCS "/sys/block/zram0/max_comp_streams" +#define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) @@ -1373,6 +1374,70 @@ int fs_mgr_do_tmpfs_mount(const char *n_name) return 0; } +static bool InstallZramDevice(const std::string& device) { + if (!android::base::WriteStringToFile(device, ZRAM_BACK_DEV)) { + PERROR << "Cannot write " << device << " in: " << ZRAM_BACK_DEV; + return false; + } + LINFO << "Success to set " << device << " to " << ZRAM_BACK_DEV; + return true; +} + +static bool PrepareZramDevice(const std::string& loop, off64_t size, const std::string& bdev) { + if (loop.empty() && bdev.empty()) return true; + + if (bdev.length()) { + return InstallZramDevice(bdev); + } + + // Get free loopback + unique_fd loop_fd(TEMP_FAILURE_RETRY(open("/dev/loop-control", O_RDWR | O_CLOEXEC))); + if (loop_fd.get() == -1) { + PERROR << "Cannot open loop-control"; + return false; + } + + int num = ioctl(loop_fd.get(), LOOP_CTL_GET_FREE); + if (num == -1) { + PERROR << "Cannot get free loop slot"; + return false; + } + + // Prepare target path + unique_fd target_fd(TEMP_FAILURE_RETRY(open(loop.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0664))); + if (target_fd.get() == -1) { + PERROR << "Cannot open target path: " << loop; + return false; + } + if (fallocate(target_fd.get(), 0, 0, size) < 0) { + PERROR << "Cannot truncate target path: " << loop; + return false; + } + + // Connect loopback (device_fd) to target path (target_fd) + std::string device = android::base::StringPrintf("/dev/block/loop%d", num); + unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC))); + if (device_fd.get() == -1) { + PERROR << "Cannot open /dev/block/loop" << num; + return false; + } + + if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get())) { + PERROR << "Cannot set loopback to target path"; + return false; + } + + // set block size & direct IO + if (ioctl(device_fd.get(), LOOP_SET_BLOCK_SIZE, 4096)) { + PWARNING << "Cannot set 4KB blocksize to /dev/block/loop" << num; + } + if (ioctl(device_fd.get(), LOOP_SET_DIRECT_IO, 1)) { + PWARNING << "Cannot set direct_io to /dev/block/loop" << num; + } + + return InstallZramDevice(device); +} + bool fs_mgr_swapon_all(const Fstab& fstab) { bool ret = true; for (const auto& entry : fstab) { @@ -1381,6 +1446,10 @@ bool fs_mgr_swapon_all(const Fstab& fstab) { continue; } + if (!PrepareZramDevice(entry.zram_loopback_path, entry.zram_loopback_size, entry.zram_backing_dev_path)) { + LERROR << "Skipping losetup for '" << entry.blk_device << "'"; + } + if (entry.zram_size > 0) { // A zram_size was specified, so we need to configure the // device. There is no point in having multiple zram devices diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp index 257603537..c9f34a73b 100644 --- a/fs_mgr/fs_mgr_fstab.cpp +++ b/fs_mgr/fs_mgr_fstab.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,9 @@ struct fs_mgr_flag_values { std::string key_dir; std::string verity_loc; std::string sysfs_path; + std::string zram_loopback_path; + uint64_t zram_loopback_size = 512 * 1024 * 1024; // 512MB by default + std::string zram_backing_dev_path; off64_t part_length = 0; std::string label; int partnum = -1; @@ -118,6 +122,9 @@ static struct flag_list fs_mgr_flags[] = { {"checkpoint=block", MF_CHECKPOINT_BLK}, {"checkpoint=fs", MF_CHECKPOINT_FS}, {"slotselect_other", MF_SLOTSELECT_OTHER}, + {"zram_loopback_path=", MF_ZRAM_LOOPBACK_PATH}, + {"zram_loopback_size=", MF_ZRAM_LOOPBACK_SIZE}, + {"zram_backing_dev_path=", MF_ZRAM_BACKING_DEV_PATH}, {0, 0}, }; @@ -345,6 +352,16 @@ static uint64_t parse_flags(char* flags, struct flag_list* fl, struct fs_mgr_fla } else if (flag == MF_SYSFS) { /* The path to trigger device gc by idle-maint of vold. */ flag_vals->sysfs_path = arg; + } else if (flag == MF_ZRAM_LOOPBACK_PATH) { + /* The path to use loopback for zram. */ + flag_vals->zram_loopback_path = arg; + } else if (flag == MF_ZRAM_LOOPBACK_SIZE) { + if (!android::base::ParseByteCount(arg, &flag_vals->zram_loopback_size)) { + LERROR << "Warning: zram_loopback_size = flag malformed"; + } + } else if (flag == MF_ZRAM_BACKING_DEV_PATH) { + /* The path to use loopback for zram. */ + flag_vals->zram_backing_dev_path = arg; } break; } @@ -570,6 +587,9 @@ static bool fs_mgr_read_fstab_file(FILE* fstab_file, bool proc_mounts, Fstab* fs entry.logical_blk_size = flag_vals.logical_blk_size; entry.sysfs_path = std::move(flag_vals.sysfs_path); entry.vbmeta_partition = std::move(flag_vals.vbmeta_partition); + entry.zram_loopback_path = std::move(flag_vals.zram_loopback_path); + entry.zram_loopback_size = std::move(flag_vals.zram_loopback_size); + entry.zram_backing_dev_path = std::move(flag_vals.zram_backing_dev_path); if (entry.fs_mgr_flags.logical) { entry.logical_partition_name = entry.blk_device; } @@ -811,6 +831,8 @@ void fs_mgr_free_fstab(struct fstab *fstab) free(fstab->recs[i].key_dir); free(fstab->recs[i].label); free(fstab->recs[i].sysfs_path); + free(fstab->recs[i].zram_loopback_path); + free(fstab->recs[i].zram_backing_dev_path); } /* Free the fstab_recs array created by calloc(3) */ @@ -908,6 +930,9 @@ FstabEntry FstabRecToFstabEntry(const fstab_rec* fstab_rec) { entry.erase_blk_size = fstab_rec->erase_blk_size; entry.logical_blk_size = fstab_rec->logical_blk_size; entry.sysfs_path = fstab_rec->sysfs_path; + entry.zram_loopback_path = fstab_rec->zram_loopback_path; + entry.zram_loopback_size = fstab_rec->zram_loopback_size; + entry.zram_backing_dev_path = fstab_rec->zram_backing_dev_path; return entry; } @@ -951,6 +976,9 @@ fstab* FstabToLegacyFstab(const Fstab& fstab) { legacy_fstab->recs[i].erase_blk_size = fstab[i].erase_blk_size; legacy_fstab->recs[i].logical_blk_size = fstab[i].logical_blk_size; legacy_fstab->recs[i].sysfs_path = strdup(fstab[i].sysfs_path.c_str()); + legacy_fstab->recs[i].zram_loopback_path = strdup(fstab[i].zram_loopback_path.c_str()); + legacy_fstab->recs[i].zram_loopback_size = fstab[i].zram_loopback_size; + legacy_fstab->recs[i].zram_backing_dev_path = strdup(fstab[i].zram_backing_dev_path.c_str()); } return legacy_fstab; } diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h index 7842ca2ff..39ceff72c 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h @@ -122,6 +122,12 @@ 0x80000000 #define MF_SLOTSELECT_OTHER \ 0x100000000 +#define MF_ZRAM_LOOPBACK_PATH \ + 0x200000000 +#define MF_ZRAM_LOOPBACK_SIZE \ + 0x400000000 +#define MF_ZRAM_BACKING_DEV_PATH \ + 0x800000000 // clang-format on #define DM_BUF_SIZE 4096 diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h index 6643c0d80..5d8496d67 100644 --- a/fs_mgr/include_fstab/fstab/fstab.h +++ b/fs_mgr/include_fstab/fstab/fstab.h @@ -59,6 +59,9 @@ struct fstab_rec { off64_t erase_blk_size; off64_t logical_blk_size; char* sysfs_path; + char* zram_loopback_path; + uint64_t zram_loopback_size; + char* zram_backing_dev_path; }; struct fstab* fs_mgr_read_fstab_default(); @@ -119,6 +122,9 @@ struct FstabEntry { off64_t logical_blk_size = 0; std::string sysfs_path; std::string vbmeta_partition; + std::string zram_loopback_path; + uint64_t zram_loopback_size; + std::string zram_backing_dev_path; // TODO: Remove this union once fstab_rec is deprecated. It only serves as a // convenient way to convert between fstab_rec::fs_mgr_flags and these bools. diff --git a/init/reboot.cpp b/init/reboot.cpp index 008a868c8..c11ea103c 100644 --- a/init/reboot.cpp +++ b/init/reboot.cpp @@ -20,9 +20,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -58,6 +60,7 @@ using android::base::Split; using android::base::StringPrintf; using android::base::Timer; +using android::base::unique_fd; namespace android { namespace init { @@ -285,6 +288,48 @@ static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeo return stat; } +// zram is able to use backing device on top of a loopback device. +// In order to unmount /data successfully, we have to kill the loopback device first +#define ZRAM_DEVICE "/dev/block/zram0" +#define ZRAM_RESET "/sys/block/zram0/reset" +#define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev" +static void KillZramBackingDevice() { + std::string backing_dev; + if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return; + + if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return; + + // cut the last "\n" + backing_dev.erase(backing_dev.length() - 1); + + // shutdown zram handle + Timer swap_timer; + LOG(INFO) << "swapoff() start..."; + if (swapoff(ZRAM_DEVICE) == -1) { + LOG(ERROR) << "zram_backing_dev: swapoff (" << backing_dev << ")" << " failed"; + return; + } + LOG(INFO) << "swapoff() took " << swap_timer;; + + if (!android::base::WriteStringToFile("1", ZRAM_RESET)) { + LOG(ERROR) << "zram_backing_dev: reset (" << backing_dev << ")" << " failed"; + return; + } + + // clear loopback device + unique_fd loop(TEMP_FAILURE_RETRY(open(backing_dev.c_str(), O_RDWR | O_CLOEXEC))); + if (loop.get() < 0) { + LOG(ERROR) << "zram_backing_dev: open(" << backing_dev << ")" << " failed"; + return; + } + + if (ioctl(loop.get(), LOOP_CLR_FD, 0) < 0) { + LOG(ERROR) << "zram_backing_dev: loop_clear (" << backing_dev << ")" << " failed"; + return; + } + LOG(INFO) << "zram_backing_dev: `" << backing_dev << "` is cleared successfully."; +} + //* Reboot / shutdown the system. // cmd ANDROID_RB_* as defined in android_reboot.h // reason Reason string like "reboot", "shutdown,userrequested" @@ -427,6 +472,9 @@ static void DoReboot(unsigned int cmd, const std::string& reason, const std::str sync(); LOG(INFO) << "sync() before umount took" << sync_timer; } + // 5. drop caches and disable zram backing device, if exist + KillZramBackingDevice(); + UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration()); // Follow what linux shutdown is doing: one more sync with little bit delay {