Merge "ueventd: Add support for updating permissions on bind"
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 34049d4..e32fde0 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -563,6 +563,7 @@
"libsnapshot_snapuserd",
"libcutils_sockets",
"libz",
+ "libfs_mgr",
"libdm",
],
header_libs: [
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index 4db6584..35020f4 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -525,6 +525,165 @@
ASSERT_TRUE(iter->Done());
}
+TEST_F(CowTest, ClusterTest) {
+ CowOptions options;
+ options.cluster_ops = 4;
+ auto writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+
+ ASSERT_TRUE(writer->AddLabel(4));
+
+ ASSERT_TRUE(writer->AddZeroBlocks(50, 2)); // Cluster split in middle
+
+ ASSERT_TRUE(writer->AddLabel(5));
+
+ ASSERT_TRUE(writer->AddCopy(5, 6));
+
+ // Cluster split
+
+ ASSERT_TRUE(writer->AddLabel(6));
+
+ ASSERT_TRUE(writer->Finalize()); // No data for cluster, so no cluster split needed
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ // Read back all ops
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ StringSink sink;
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
+
+ iter->Next();
+ sink.Reset();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowLabelOp);
+ ASSERT_EQ(op->source, 4);
+
+ iter->Next();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowZeroOp);
+
+ iter->Next();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowClusterOp);
+
+ iter->Next();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowZeroOp);
+
+ iter->Next();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowLabelOp);
+ ASSERT_EQ(op->source, 5);
+
+ iter->Next();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowCopyOp);
+
+ iter->Next();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowClusterOp);
+
+ iter->Next();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowLabelOp);
+ ASSERT_EQ(op->source, 6);
+
+ iter->Next();
+
+ ASSERT_TRUE(iter->Done());
+}
+
+TEST_F(CowTest, ClusterAppendTest) {
+ CowOptions options;
+ options.cluster_ops = 3;
+ auto writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+ ASSERT_TRUE(writer->AddLabel(50));
+ ASSERT_TRUE(writer->Finalize()); // Adds a cluster op, should be dropped on append
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 50));
+
+ std::string data2 = "More data!";
+ data2.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
+ ASSERT_TRUE(writer->Finalize()); // Adds a cluster op
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ struct stat buf;
+ ASSERT_EQ(fstat(cow_->fd, &buf), 0);
+ ASSERT_EQ(buf.st_size, writer->GetCowSize());
+
+ // Read back both operations, plus cluster op at end
+ CowReader reader;
+ uint64_t label;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_TRUE(reader.GetLastLabel(&label));
+ ASSERT_EQ(label, 50);
+
+ StringSink sink;
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+ ASSERT_EQ(op->type, kCowLabelOp);
+ ASSERT_EQ(op->source, 50);
+
+ iter->Next();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data2);
+
+ iter->Next();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowClusterOp);
+
+ iter->Next();
+
+ ASSERT_TRUE(iter->Done());
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_format.cpp b/fs_mgr/libsnapshot/cow_format.cpp
index 49ba11f..0753c49 100644
--- a/fs_mgr/libsnapshot/cow_format.cpp
+++ b/fs_mgr/libsnapshot/cow_format.cpp
@@ -35,6 +35,10 @@
os << "kCowFooterOp, ";
else if (op.type == kCowLabelOp)
os << "kCowLabelOp, ";
+ else if (op.type == kCowClusterOp)
+ os << "kCowClusterOp ";
+ else if (op.type == kCowFooterOp)
+ os << "kCowFooterOp ";
else
os << (int)op.type << "?,";
os << "compression:";
@@ -52,11 +56,35 @@
return os;
}
-int64_t GetNextOpOffset(const CowOperation& op) {
- if (op.type == kCowReplaceOp)
+int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_ops) {
+ if (op.type == kCowClusterOp) {
+ return op.source;
+ } else if (op.type == kCowReplaceOp && cluster_ops == 0) {
return op.data_length;
- else
+ } else {
return 0;
+ }
+}
+
+int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_ops) {
+ if (op.type == kCowClusterOp) {
+ return cluster_ops * sizeof(CowOperation);
+ } else if (cluster_ops == 0) {
+ return sizeof(CowOperation);
+ } else {
+ return 0;
+ }
+}
+
+bool IsMetadataOp(const CowOperation& op) {
+ switch (op.type) {
+ case kCowLabelOp:
+ case kCowClusterOp:
+ case kCowFooterOp:
+ return true;
+ default:
+ return false;
+ }
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 6b7ada5..3fbc5f3 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -81,6 +81,24 @@
<< sizeof(CowFooter);
return false;
}
+ if (header_.op_size != sizeof(CowOperation)) {
+ LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
+ << sizeof(CowOperation);
+ return false;
+ }
+ if (header_.cluster_ops == 1) {
+ LOG(ERROR) << "Clusters must contain at least two operations to function.";
+ return false;
+ }
+ if (header_.op_size != sizeof(CowOperation)) {
+ LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
+ << sizeof(CowOperation);
+ return false;
+ }
+ if (header_.cluster_ops == 1) {
+ LOG(ERROR) << "Clusters must contain at least two operations to function.";
+ return false;
+ }
if ((header_.major_version != kCowVersionMajor) ||
(header_.minor_version != kCowVersionMinor)) {
@@ -103,45 +121,64 @@
}
auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
+ uint64_t current_op_num = 0;
+ uint64_t cluster_ops = header_.cluster_ops ?: 1;
+ bool done = false;
- // Alternating op and data
- while (true) {
- ops_buffer->emplace_back();
- if (!android::base::ReadFully(fd_, &ops_buffer->back(), sizeof(CowOperation))) {
+ // Alternating op clusters and data
+ while (!done) {
+ uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
+ if (to_add == 0) break;
+ ops_buffer->resize(current_op_num + to_add);
+ if (!android::base::ReadFully(fd_, &ops_buffer->data()[current_op_num],
+ to_add * sizeof(CowOperation))) {
PLOG(ERROR) << "read op failed";
return false;
}
+ // Parse current cluster to find start of next cluster
+ while (current_op_num < ops_buffer->size()) {
+ auto& current_op = ops_buffer->data()[current_op_num];
+ current_op_num++;
+ pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
- auto& current_op = ops_buffer->back();
- off_t offs = lseek(fd_.get(), GetNextOpOffset(current_op), SEEK_CUR);
- if (offs < 0) {
+ if (current_op.type == kCowClusterOp) {
+ break;
+ } else if (current_op.type == kCowLabelOp) {
+ last_label_ = {current_op.source};
+
+ // If we reach the requested label, stop reading.
+ if (label && label.value() == current_op.source) {
+ done = true;
+ break;
+ }
+ } else if (current_op.type == kCowFooterOp) {
+ footer_.emplace();
+ CowFooter* footer = &footer_.value();
+ memcpy(&footer_->op, ¤t_op, sizeof(footer->op));
+ off_t offs = lseek(fd_.get(), pos, SEEK_SET);
+ if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
+ PLOG(ERROR) << "lseek next op failed";
+ return false;
+ }
+ if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
+ LOG(ERROR) << "Could not read COW footer";
+ return false;
+ }
+
+ // Drop the footer from the op stream.
+ current_op_num--;
+ done = true;
+ break;
+ }
+ }
+
+ // Position for next cluster read
+ off_t offs = lseek(fd_.get(), pos, SEEK_SET);
+ if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
PLOG(ERROR) << "lseek next op failed";
return false;
}
- pos = static_cast<uint64_t>(offs);
-
- if (current_op.type == kCowLabelOp) {
- last_label_ = {current_op.source};
-
- // If we reach the requested label, stop reading.
- if (label && label.value() == current_op.source) {
- break;
- }
- } else if (current_op.type == kCowFooterOp) {
- footer_.emplace();
-
- CowFooter* footer = &footer_.value();
- memcpy(&footer_->op, ¤t_op, sizeof(footer->op));
-
- if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
- LOG(ERROR) << "Could not read COW footer";
- return false;
- }
-
- // Drop the footer from the op stream.
- ops_buffer->pop_back();
- break;
- }
+ ops_buffer->resize(current_op_num);
}
// To successfully parse a COW file, we need either:
@@ -189,29 +226,136 @@
LOG(INFO) << "No COW Footer, recovered data";
}
- if (header_.num_merge_ops > 0) {
- uint64_t merge_ops = header_.num_merge_ops;
- uint64_t metadata_ops = 0;
- uint64_t current_op_num = 0;
-
- CHECK(ops_buffer->size() >= merge_ops);
- while (merge_ops) {
- auto& current_op = ops_buffer->data()[current_op_num];
- if (current_op.type == kCowLabelOp || current_op.type == kCowFooterOp) {
- metadata_ops += 1;
- } else {
- merge_ops -= 1;
- }
- current_op_num += 1;
- }
- ops_buffer->erase(ops_buffer.get()->begin(),
- ops_buffer.get()->begin() + header_.num_merge_ops + metadata_ops);
- }
-
ops_ = ops_buffer;
return true;
}
+void CowReader::InitializeMerge() {
+ uint64_t num_copy_ops = 0;
+
+ // Remove all the metadata operations
+ ops_->erase(std::remove_if(ops_.get()->begin(), ops_.get()->end(),
+ [](CowOperation& op) { return IsMetadataOp(op); }),
+ ops_.get()->end());
+
+ // We will re-arrange the vector in such a way that
+ // kernel can batch merge. Ex:
+ //
+ // Existing COW format; All the copy operations
+ // are at the beginning.
+ // =======================================
+ // Copy-op-1 - cow_op->new_block = 1
+ // Copy-op-2 - cow_op->new_block = 2
+ // Copy-op-3 - cow_op->new_block = 3
+ // Replace-op-4 - cow_op->new_block = 6
+ // Replace-op-5 - cow_op->new_block = 4
+ // Replace-op-6 - cow_op->new_block = 8
+ // Replace-op-7 - cow_op->new_block = 9
+ // Zero-op-8 - cow_op->new_block = 7
+ // Zero-op-9 - cow_op->new_block = 5
+ // =======================================
+ //
+ // First find the operation which isn't a copy-op
+ // and then sort all the operations in descending order
+ // with the key being cow_op->new_block (source block)
+ //
+ // The data-structure will look like:
+ //
+ // =======================================
+ // Copy-op-1 - cow_op->new_block = 1
+ // Copy-op-2 - cow_op->new_block = 2
+ // Copy-op-3 - cow_op->new_block = 3
+ // Replace-op-7 - cow_op->new_block = 9
+ // Replace-op-6 - cow_op->new_block = 8
+ // Zero-op-8 - cow_op->new_block = 7
+ // Replace-op-4 - cow_op->new_block = 6
+ // Zero-op-9 - cow_op->new_block = 5
+ // Replace-op-5 - cow_op->new_block = 4
+ // =======================================
+ //
+ // Daemon will read the above data-structure in reverse-order
+ // when reading metadata. Thus, kernel will get the metadata
+ // in the following order:
+ //
+ // ========================================
+ // Replace-op-5 - cow_op->new_block = 4
+ // Zero-op-9 - cow_op->new_block = 5
+ // Replace-op-4 - cow_op->new_block = 6
+ // Zero-op-8 - cow_op->new_block = 7
+ // Replace-op-6 - cow_op->new_block = 8
+ // Replace-op-7 - cow_op->new_block = 9
+ // Copy-op-3 - cow_op->new_block = 3
+ // Copy-op-2 - cow_op->new_block = 2
+ // Copy-op-1 - cow_op->new_block = 1
+ // ===========================================
+ //
+ // When merging begins, kernel will start from the last
+ // metadata which was read: In the above format, Copy-op-1
+ // will be the first merge operation.
+ //
+ // Now, batching of the merge operations happens only when
+ // 1: origin block numbers in the base device are contiguous
+ // (cow_op->new_block) and,
+ // 2: cow block numbers which are assigned by daemon in ReadMetadata()
+ // are contiguous. These are monotonically increasing numbers.
+ //
+ // When both (1) and (2) are true, kernel will batch merge the operations.
+ // However, we do not want copy operations to be batch merged as
+ // a crash or system reboot during an overlapping copy can drive the device
+ // to a corrupted state. Hence, merging of copy operations should always be
+ // done as a individual 4k block. In the above case, since the
+ // cow_op->new_block numbers are contiguous, we will ensure that the
+ // cow block numbers assigned in ReadMetadata() for these respective copy
+ // operations are not contiguous forcing kernel to issue merge for each
+ // copy operations without batch merging.
+ //
+ // For all the other operations viz. Replace and Zero op, the cow block
+ // numbers assigned by daemon will be contiguous allowing kernel to batch
+ // merge.
+ //
+ // The final format after assiging COW block numbers by the daemon will
+ // look something like:
+ //
+ // =========================================================
+ // Replace-op-5 - cow_op->new_block = 4 cow-block-num = 2
+ // Zero-op-9 - cow_op->new_block = 5 cow-block-num = 3
+ // Replace-op-4 - cow_op->new_block = 6 cow-block-num = 4
+ // Zero-op-8 - cow_op->new_block = 7 cow-block-num = 5
+ // Replace-op-6 - cow_op->new_block = 8 cow-block-num = 6
+ // Replace-op-7 - cow_op->new_block = 9 cow-block-num = 7
+ // Copy-op-3 - cow_op->new_block = 3 cow-block-num = 9
+ // Copy-op-2 - cow_op->new_block = 2 cow-block-num = 11
+ // Copy-op-1 - cow_op->new_block = 1 cow-block-num = 13
+ // ==========================================================
+ //
+ // Merge sequence will look like:
+ //
+ // Merge-1 - Copy-op-1
+ // Merge-2 - Copy-op-2
+ // Merge-3 - Copy-op-3
+ // Merge-4 - Batch-merge {Replace-op-7, Replace-op-6, Zero-op-8,
+ // Replace-op-4, Zero-op-9, Replace-op-5 }
+ //==============================================================
+
+ for (uint64_t i = 0; i < ops_->size(); i++) {
+ auto& current_op = ops_->data()[i];
+ if (current_op.type != kCowCopyOp) {
+ break;
+ }
+ num_copy_ops += 1;
+ }
+
+ std::sort(ops_.get()->begin() + num_copy_ops, ops_.get()->end(),
+ [](CowOperation& op1, CowOperation& op2) -> bool {
+ return op1.new_block > op2.new_block;
+ });
+
+ if (header_.num_merge_ops > 0) {
+ CHECK(ops_->size() >= header_.num_merge_ops);
+ ops_->erase(ops_.get()->begin(), ops_.get()->begin() + header_.num_merge_ops);
+ }
+}
+
bool CowReader::GetHeader(CowHeader* header) {
*header = header_;
return true;
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 5483fd0..16d9313 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -12,10 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <fcntl.h>
#include <linux/fs.h>
+#include <linux/memfd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
+#include <sys/syscall.h>
#include <sys/types.h>
+#include <unistd.h>
#include <chrono>
#include <iostream>
@@ -24,7 +28,9 @@
#include <android-base/file.h>
#include <android-base/unique_fd.h>
+#include <fs_mgr/file_wait.h>
#include <gtest/gtest.h>
+#include <libdm/dm.h>
#include <libdm/loop_control.h>
#include <libsnapshot/cow_writer.h>
#include <libsnapshot/snapuserd_client.h>
@@ -37,111 +43,203 @@
using android::base::unique_fd;
using LoopDevice = android::dm::LoopDevice;
using namespace std::chrono_literals;
+using namespace android::dm;
+using namespace std;
-class SnapuserdTest : public ::testing::Test {
- protected:
- void SetUp() override {
- // TODO: Daemon started through first stage
- // init does not have permission to read files
- // from /data/nativetest.
- system("setenforce 0");
- cow_system_ = std::make_unique<TemporaryFile>();
- ASSERT_GE(cow_system_->fd, 0) << strerror(errno);
+static constexpr char kSnapuserdSocketTest[] = "snapuserdTest";
- cow_product_ = std::make_unique<TemporaryFile>();
- ASSERT_GE(cow_product_->fd, 0) << strerror(errno);
+class TempDevice {
+ public:
+ TempDevice(const std::string& name, const DmTable& table)
+ : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
+ valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));
+ }
+ TempDevice(TempDevice&& other) noexcept
+ : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
+ other.valid_ = false;
+ }
+ ~TempDevice() {
+ if (valid_) {
+ dm_.DeleteDevice(name_);
+ }
+ }
+ bool Destroy() {
+ if (!valid_) {
+ return false;
+ }
+ valid_ = false;
+ return dm_.DeleteDevice(name_);
+ }
+ const std::string& path() const { return path_; }
+ const std::string& name() const { return name_; }
+ bool valid() const { return valid_; }
- cow_system_1_ = std::make_unique<TemporaryFile>();
- ASSERT_GE(cow_system_1_->fd, 0) << strerror(errno);
+ TempDevice(const TempDevice&) = delete;
+ TempDevice& operator=(const TempDevice&) = delete;
- cow_product_1_ = std::make_unique<TemporaryFile>();
- ASSERT_GE(cow_product_1_->fd, 0) << strerror(errno);
-
- std::string path = android::base::GetExecutableDirectory();
-
- system_a_ = std::make_unique<TemporaryFile>(path);
- ASSERT_GE(system_a_->fd, 0) << strerror(errno);
-
- product_a_ = std::make_unique<TemporaryFile>(path);
- ASSERT_GE(product_a_->fd, 0) << strerror(errno);
-
- size_ = 100_MiB;
+ TempDevice& operator=(TempDevice&& other) noexcept {
+ name_ = other.name_;
+ valid_ = other.valid_;
+ other.valid_ = false;
+ return *this;
}
- void TearDown() override {
- system("setenforce 1");
- cow_system_ = nullptr;
- cow_product_ = nullptr;
-
- cow_system_1_ = nullptr;
- cow_product_1_ = nullptr;
- }
-
- std::unique_ptr<TemporaryFile> system_a_;
- std::unique_ptr<TemporaryFile> product_a_;
-
- std::unique_ptr<LoopDevice> system_a_loop_;
- std::unique_ptr<LoopDevice> product_a_loop_;
-
- std::unique_ptr<TemporaryFile> cow_system_;
- std::unique_ptr<TemporaryFile> cow_product_;
-
- std::unique_ptr<TemporaryFile> cow_system_1_;
- std::unique_ptr<TemporaryFile> cow_product_1_;
-
- unique_fd sys_fd_;
- unique_fd product_fd_;
- size_t size_;
-
- int system_blksize_;
- int product_blksize_;
- std::string system_device_name_;
- std::string product_device_name_;
-
- std::string system_device_ctrl_name_;
- std::string product_device_ctrl_name_;
-
- std::unique_ptr<uint8_t[]> random_buffer_1_;
- std::unique_ptr<uint8_t[]> random_buffer_2_;
- std::unique_ptr<uint8_t[]> zero_buffer_;
- std::unique_ptr<uint8_t[]> system_buffer_;
- std::unique_ptr<uint8_t[]> product_buffer_;
-
- void Init();
- void InitCowDevices();
- void InitDaemon();
- void CreateCowDevice(std::unique_ptr<TemporaryFile>& cow);
- void CreateSystemDmUser(std::unique_ptr<TemporaryFile>& cow);
- void CreateProductDmUser(std::unique_ptr<TemporaryFile>& cow);
- void DeleteDmUser(std::unique_ptr<TemporaryFile>& cow, std::string snapshot_device);
- void StartSnapuserdDaemon();
- void CreateSnapshotDevices();
- void SwitchSnapshotDevices();
-
- std::string GetSystemControlPath() {
- return std::string("/dev/dm-user/") + system_device_ctrl_name_;
- }
- std::string GetProductControlPath() {
- return std::string("/dev/dm-user/") + product_device_ctrl_name_;
- }
-
- void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>& buffer);
- std::unique_ptr<SnapuserdClient> client_;
+ private:
+ DeviceMapper& dm_;
+ std::string name_;
+ std::string path_;
+ bool valid_;
};
-void SnapuserdTest::Init() {
+class CowSnapuserdTest final {
+ public:
+ bool Setup();
+ bool Merge();
+ void ValidateMerge();
+ void ReadSnapshotDeviceAndValidate();
+ void Shutdown();
+
+ std::string snapshot_dev() const { return snapshot_dev_->path(); }
+
+ static const uint64_t kSectorSize = 512;
+
+ private:
+ void SetupImpl();
+ void MergeImpl();
+ void CreateCowDevice();
+ void CreateBaseDevice();
+ void InitCowDevice();
+ void SetDeviceControlName();
+ void InitDaemon();
+ void CreateDmUserDevice();
+ void StartSnapuserdDaemon();
+ void CreateSnapshotDevice();
+ unique_fd CreateTempFile(const std::string& name, size_t size);
+
+ unique_ptr<LoopDevice> base_loop_;
+ unique_ptr<TempDevice> dmuser_dev_;
+ unique_ptr<TempDevice> snapshot_dev_;
+
+ std::string system_device_ctrl_name_;
+ std::string system_device_name_;
+
+ unique_fd base_fd_;
+ std::unique_ptr<TemporaryFile> cow_system_;
+ std::unique_ptr<SnapuserdClient> client_;
+ std::unique_ptr<uint8_t[]> orig_buffer_;
+ std::unique_ptr<uint8_t[]> merged_buffer_;
+ bool setup_ok_ = false;
+ bool merge_ok_ = false;
+ size_t size_ = 1_MiB;
+ int cow_num_sectors_;
+ int total_base_size_;
+};
+
+unique_fd CowSnapuserdTest::CreateTempFile(const std::string& name, size_t size) {
+ unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));
+ if (fd < 0) {
+ return {};
+ }
+ if (size) {
+ if (ftruncate(fd, size) < 0) {
+ perror("ftruncate");
+ return {};
+ }
+ if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+ perror("fcntl");
+ return {};
+ }
+ }
+ return fd;
+}
+
+void CowSnapuserdTest::Shutdown() {
+ ASSERT_TRUE(client_->StopSnapuserd());
+ ASSERT_TRUE(snapshot_dev_->Destroy());
+ ASSERT_TRUE(dmuser_dev_->Destroy());
+}
+
+bool CowSnapuserdTest::Setup() {
+ SetupImpl();
+ return setup_ok_;
+}
+
+void CowSnapuserdTest::StartSnapuserdDaemon() {
+ pid_t pid = fork();
+ ASSERT_GE(pid, 0);
+ if (pid == 0) {
+ std::string arg0 = "/system/bin/snapuserd";
+ std::string arg1 = kSnapuserdSocketTest;
+ char* const argv[] = {arg0.data(), arg1.data(), nullptr};
+ ASSERT_GE(execv(arg0.c_str(), argv), 0);
+ } else {
+ client_ = SnapuserdClient::Connect(kSnapuserdSocketTest, 10s);
+ ASSERT_NE(client_, nullptr);
+ }
+}
+
+void CowSnapuserdTest::CreateBaseDevice() {
unique_fd rnd_fd;
- loff_t offset = 0;
- std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(1_MiB);
+
+ total_base_size_ = (size_ * 4);
+ base_fd_ = CreateTempFile("base_device", total_base_size_);
+ ASSERT_GE(base_fd_, 0);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
- random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
- random_buffer_2_ = std::make_unique<uint8_t[]>(size_);
- system_buffer_ = std::make_unique<uint8_t[]>(size_);
- product_buffer_ = std::make_unique<uint8_t[]>(size_);
- zero_buffer_ = std::make_unique<uint8_t[]>(size_);
+ std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(1_MiB);
+
+ for (size_t j = 0; j < ((total_base_size_) / 1_MiB); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
+ ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), 1_MiB), true);
+ }
+
+ ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
+
+ base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
+ ASSERT_TRUE(base_loop_->valid());
+}
+
+void CowSnapuserdTest::ReadSnapshotDeviceAndValidate() {
+ unique_fd snapshot_fd(open(snapshot_dev_->path().c_str(), O_RDONLY));
+ ASSERT_TRUE(snapshot_fd > 0);
+
+ std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
+
+ // COPY
+ loff_t offset = 0;
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);
+
+ // REPLACE
+ offset += size_;
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0);
+
+ // ZERO
+ offset += size_;
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0);
+
+ // REPLACE
+ offset += size_;
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
+}
+
+void CowSnapuserdTest::CreateCowDevice() {
+ unique_fd rnd_fd;
+ loff_t offset = 0;
+
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
+
+ rnd_fd.reset(open("/dev/random", O_RDONLY));
+ ASSERT_TRUE(rnd_fd > 0);
+
+ std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+ std::unique_ptr<uint8_t[]> random_buffer_2_ = std::make_unique<uint8_t[]>(size_);
// Fill random data
for (size_t j = 0; j < (size_ / 1_MiB); j++) {
@@ -154,77 +252,32 @@
offset += 1_MiB;
}
- for (size_t j = 0; j < (800_MiB / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
- ASSERT_EQ(android::base::WriteFully(system_a_->fd, random_buffer.get(), 1_MiB), true);
- }
-
- for (size_t j = 0; j < (800_MiB / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
- ASSERT_EQ(android::base::WriteFully(product_a_->fd, random_buffer.get(), 1_MiB), true);
- }
-
- // Create loopback devices
- system_a_loop_ = std::make_unique<LoopDevice>(std::string(system_a_->path), 10s);
- ASSERT_TRUE(system_a_loop_->valid());
-
- product_a_loop_ = std::make_unique<LoopDevice>(std::string(product_a_->path), 10s);
- ASSERT_TRUE(product_a_loop_->valid());
-
- sys_fd_.reset(open(system_a_loop_->device().c_str(), O_RDONLY));
- ASSERT_TRUE(sys_fd_ > 0);
-
- product_fd_.reset(open(product_a_loop_->device().c_str(), O_RDONLY));
- ASSERT_TRUE(product_fd_ > 0);
-
- // Read from system partition from offset 0 of size 100MB
- ASSERT_EQ(ReadFullyAtOffset(sys_fd_, system_buffer_.get(), size_, 0), true);
-
- // Read from product partition from offset 0 of size 100MB
- ASSERT_EQ(ReadFullyAtOffset(product_fd_, product_buffer_.get(), size_, 0), true);
-}
-
-void SnapuserdTest::CreateCowDevice(std::unique_ptr<TemporaryFile>& cow) {
- //================Create a COW file with the following operations===========
- //
- // Create COW file which is gz compressed
- //
- // 0-100 MB of replace operation with random data
- // 100-200 MB of copy operation
- // 200-300 MB of zero operation
- // 300-400 MB of replace operation with random data
-
CowOptions options;
options.compression = "gz";
CowWriter writer(options);
- ASSERT_TRUE(writer.Initialize(cow->fd));
-
- // Write 100MB random data to COW file which is gz compressed from block 0
- ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), size_));
+ ASSERT_TRUE(writer.Initialize(cow_system_->fd));
size_t num_blocks = size_ / options.block_size;
- size_t blk_start_copy = num_blocks;
- size_t blk_end_copy = blk_start_copy + num_blocks;
+ size_t blk_src_copy = num_blocks;
+ size_t blk_end_copy = blk_src_copy + num_blocks;
size_t source_blk = 0;
- // Copy blocks - source_blk starts from 0 as snapuserd
- // has to read from block 0 in system_a partition
- //
- // This initializes copy operation from block 0 of size 100 MB from
- // /dev/block/mapper/system_a or product_a
- for (size_t i = blk_start_copy; i < blk_end_copy; i++) {
- ASSERT_TRUE(writer.AddCopy(i, source_blk));
+ while (source_blk < num_blocks) {
+ ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
source_blk += 1;
+ blk_src_copy += 1;
}
- size_t blk_zero_copy_start = blk_end_copy;
+ ASSERT_EQ(blk_src_copy, blk_end_copy);
+
+ ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
+
+ size_t blk_zero_copy_start = source_blk + num_blocks;
size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
- // 100 MB filled with zeroes
ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
- // Final 100MB filled with random data which is gz compressed
size_t blk_random2_replace_start = blk_zero_copy_end;
ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_2_.get(), size_));
@@ -232,233 +285,133 @@
// Flush operations
ASSERT_TRUE(writer.Finalize());
- ASSERT_EQ(lseek(cow->fd, 0, SEEK_SET), 0);
+ // Construct the buffer required for validation
+ orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
+ std::string zero_buffer(size_, 0);
+
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);
+ memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
+ memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
+ memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_2_.get(), size_);
}
-void SnapuserdTest::CreateSystemDmUser(std::unique_ptr<TemporaryFile>& cow) {
- std::string cmd;
+void CowSnapuserdTest::InitCowDevice() {
+ cow_num_sectors_ = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path,
+ base_loop_->device());
+ ASSERT_NE(cow_num_sectors_, 0);
+}
+
+void CowSnapuserdTest::SetDeviceControlName() {
system_device_name_.clear();
system_device_ctrl_name_.clear();
- std::string str(cow->path);
+ std::string str(cow_system_->path);
std::size_t found = str.find_last_of("/\\");
ASSERT_NE(found, std::string::npos);
system_device_name_ = str.substr(found + 1);
- // Create a control device
system_device_ctrl_name_ = system_device_name_ + "-ctrl";
- cmd = "dmctl create " + system_device_name_ + " user 0 " + std::to_string(system_blksize_);
- cmd += " " + system_device_ctrl_name_;
-
- system(cmd.c_str());
}
-void SnapuserdTest::DeleteDmUser(std::unique_ptr<TemporaryFile>& cow, std::string snapshot_device) {
- std::string cmd;
+void CowSnapuserdTest::CreateDmUserDevice() {
+ DmTable dmuser_table;
+ ASSERT_TRUE(dmuser_table.AddTarget(
+ std::make_unique<DmTargetUser>(0, cow_num_sectors_, system_device_ctrl_name_)));
+ ASSERT_TRUE(dmuser_table.valid());
- cmd = "dmctl delete " + snapshot_device;
- system(cmd.c_str());
+ dmuser_dev_ = std::make_unique<TempDevice>(system_device_name_, dmuser_table);
+ ASSERT_TRUE(dmuser_dev_->valid());
+ ASSERT_FALSE(dmuser_dev_->path().empty());
- cmd.clear();
-
- std::string str(cow->path);
- std::size_t found = str.find_last_of("/\\");
- ASSERT_NE(found, std::string::npos);
- std::string device_name = str.substr(found + 1);
-
- cmd = "dmctl delete " + device_name;
-
- system(cmd.c_str());
+ auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
+ ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
}
-void SnapuserdTest::CreateProductDmUser(std::unique_ptr<TemporaryFile>& cow) {
- std::string cmd;
- product_device_name_.clear();
- product_device_ctrl_name_.clear();
-
- std::string str(cow->path);
- std::size_t found = str.find_last_of("/\\");
- ASSERT_NE(found, std::string::npos);
- product_device_name_ = str.substr(found + 1);
- product_device_ctrl_name_ = product_device_name_ + "-ctrl";
- cmd = "dmctl create " + product_device_name_ + " user 0 " + std::to_string(product_blksize_);
- cmd += " " + product_device_ctrl_name_;
-
- system(cmd.c_str());
-}
-
-void SnapuserdTest::InitCowDevices() {
- system_blksize_ = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path,
- system_a_loop_->device());
- ASSERT_NE(system_blksize_, 0);
-
- product_blksize_ = client_->InitDmUserCow(product_device_ctrl_name_, cow_product_->path,
- product_a_loop_->device());
- ASSERT_NE(product_blksize_, 0);
-}
-
-void SnapuserdTest::InitDaemon() {
+void CowSnapuserdTest::InitDaemon() {
bool ok = client_->AttachDmUser(system_device_ctrl_name_);
ASSERT_TRUE(ok);
-
- ok = client_->AttachDmUser(product_device_ctrl_name_);
- ASSERT_TRUE(ok);
}
-void SnapuserdTest::StartSnapuserdDaemon() {
- ASSERT_TRUE(EnsureSnapuserdStarted());
+void CowSnapuserdTest::CreateSnapshotDevice() {
+ DmTable snap_table;
+ ASSERT_TRUE(snap_table.AddTarget(std::make_unique<DmTargetSnapshot>(
+ 0, total_base_size_ / kSectorSize, base_loop_->device(), dmuser_dev_->path(),
+ SnapshotStorageMode::Persistent, 8)));
+ ASSERT_TRUE(snap_table.valid());
- client_ = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
- ASSERT_NE(client_, nullptr);
+ snap_table.set_readonly(true);
+
+ snapshot_dev_ = std::make_unique<TempDevice>("cowsnapuserd-test-dm-snapshot", snap_table);
+ ASSERT_TRUE(snapshot_dev_->valid());
+ ASSERT_FALSE(snapshot_dev_->path().empty());
}
-void SnapuserdTest::CreateSnapshotDevices() {
- std::string cmd;
+void CowSnapuserdTest::SetupImpl() {
+ CreateBaseDevice();
+ CreateCowDevice();
- cmd = "dmctl create system-snapshot -ro snapshot 0 " + std::to_string(system_blksize_);
- cmd += " " + system_a_loop_->device();
- cmd += " /dev/block/mapper/" + system_device_name_;
- cmd += " P 8";
-
- system(cmd.c_str());
-
- cmd.clear();
-
- cmd = "dmctl create product-snapshot -ro snapshot 0 " + std::to_string(product_blksize_);
- cmd += " " + product_a_loop_->device();
- cmd += " /dev/block/mapper/" + product_device_name_;
- cmd += " P 8";
-
- system(cmd.c_str());
-}
-
-void SnapuserdTest::SwitchSnapshotDevices() {
- std::string cmd;
-
- cmd = "dmctl create system-snapshot-1 -ro snapshot 0 " + std::to_string(system_blksize_);
- cmd += " " + system_a_loop_->device();
- cmd += " /dev/block/mapper/" + system_device_name_;
- cmd += " P 8";
-
- system(cmd.c_str());
-
- cmd.clear();
-
- cmd = "dmctl create product-snapshot-1 -ro snapshot 0 " + std::to_string(product_blksize_);
- cmd += " " + product_a_loop_->device();
- cmd += " /dev/block/mapper/" + product_device_name_;
- cmd += " P 8";
-
- system(cmd.c_str());
-}
-
-void SnapuserdTest::TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>& buffer) {
- loff_t offset = 0;
- // std::unique_ptr<uint8_t[]> buffer = std::move(buf);
-
- std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
-
- //================Start IO operation on dm-snapshot device=================
- // This will test the following paths:
- //
- // 1: IO path for all three operations and interleaving of operations.
- // 2: Merging of blocks in kernel during metadata read
- // 3: Bulk IO issued by kernel duing merge operation
-
- // Read from snapshot device of size 100MB from offset 0. This tests the
- // 1st replace operation.
- //
- // IO path:
- //
- // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_compressed_cow (replace
- // op)->decompress_cow->return
-
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
-
- // Update the offset
- offset += size_;
-
- // Compare data with random_buffer_1_.
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_1_.get(), size_), 0);
-
- // Clear the buffer
- memset(snapuserd_buffer.get(), 0, size_);
-
- // Read from snapshot device of size 100MB from offset 100MB. This tests the
- // copy operation.
- //
- // IO path:
- //
- // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_from_(system_a/product_a) partition
- // (copy op) -> return
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
-
- // Update the offset
- offset += size_;
-
- // Compare data with buffer.
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), buffer.get(), size_), 0);
-
- // Read from snapshot device of size 100MB from offset 200MB. This tests the
- // zero operation.
- //
- // IO path:
- //
- // dm-snap->dm-snap-persistent->dm-user->snapuserd->fill_memory_with_zero
- // (zero op) -> return
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
-
- // Compare data with zero filled buffer
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), zero_buffer_.get(), size_), 0);
-
- // Update the offset
- offset += size_;
-
- // Read from snapshot device of size 100MB from offset 300MB. This tests the
- // final replace operation.
- //
- // IO path:
- //
- // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_compressed_cow (replace
- // op)->decompress_cow->return
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
-
- // Compare data with random_buffer_2_.
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_2_.get(), size_), 0);
-}
-
-TEST_F(SnapuserdTest, ReadWrite) {
- unique_fd snapshot_fd;
-
- Init();
-
- CreateCowDevice(cow_system_);
- CreateCowDevice(cow_product_);
+ SetDeviceControlName();
StartSnapuserdDaemon();
- InitCowDevices();
+ InitCowDevice();
- CreateSystemDmUser(cow_system_);
- CreateProductDmUser(cow_product_);
-
+ CreateDmUserDevice();
InitDaemon();
- CreateSnapshotDevices();
+ CreateSnapshotDevice();
+ setup_ok_ = true;
+}
- snapshot_fd.reset(open("/dev/block/mapper/system-snapshot", O_RDONLY));
- ASSERT_TRUE(snapshot_fd > 0);
- TestIO(snapshot_fd, system_buffer_);
+bool CowSnapuserdTest::Merge() {
+ MergeImpl();
+ return merge_ok_;
+}
- snapshot_fd.reset(open("/dev/block/mapper/product-snapshot", O_RDONLY));
- ASSERT_TRUE(snapshot_fd > 0);
- TestIO(snapshot_fd, product_buffer_);
+void CowSnapuserdTest::MergeImpl() {
+ DmTable merge_table;
+ ASSERT_TRUE(merge_table.AddTarget(std::make_unique<DmTargetSnapshot>(
+ 0, total_base_size_ / kSectorSize, base_loop_->device(), dmuser_dev_->path(),
+ SnapshotStorageMode::Merge, 8)));
+ ASSERT_TRUE(merge_table.valid());
+ ASSERT_EQ(total_base_size_ / kSectorSize, merge_table.num_sectors());
- snapshot_fd.reset(-1);
+ DeviceMapper& dm = DeviceMapper::Instance();
+ ASSERT_TRUE(dm.LoadTableAndActivate("cowsnapuserd-test-dm-snapshot", merge_table));
- DeleteDmUser(cow_system_, "system-snapshot");
- DeleteDmUser(cow_product_, "product-snapshot");
+ while (true) {
+ vector<DeviceMapper::TargetInfo> status;
+ ASSERT_TRUE(dm.GetTableStatus("cowsnapuserd-test-dm-snapshot", &status));
+ ASSERT_EQ(status.size(), 1);
+ ASSERT_EQ(strncmp(status[0].spec.target_type, "snapshot-merge", strlen("snapshot-merge")),
+ 0);
- ASSERT_TRUE(client_->StopSnapuserd());
+ DmTargetSnapshot::Status merge_status;
+ ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(status[0].data, &merge_status));
+ ASSERT_TRUE(merge_status.error.empty());
+ if (merge_status.sectors_allocated == merge_status.metadata_sectors) {
+ break;
+ }
+
+ std::this_thread::sleep_for(250ms);
+ }
+
+ merge_ok_ = true;
+}
+
+void CowSnapuserdTest::ValidateMerge() {
+ merged_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, merged_buffer_.get(), total_base_size_, 0),
+ true);
+ ASSERT_EQ(memcmp(merged_buffer_.get(), orig_buffer_.get(), total_base_size_), 0);
+}
+
+TEST(Snapuserd_Test, Snapshot) {
+ CowSnapuserdTest harness;
+ ASSERT_TRUE(harness.Setup());
+ harness.ReadSnapshotDeviceAndValidate();
+ ASSERT_TRUE(harness.Merge());
+ harness.ValidateMerge();
+ harness.Shutdown();
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 957ba35..8535252 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -90,8 +90,10 @@
header_.minor_version = kCowVersionMinor;
header_.header_size = sizeof(CowHeader);
header_.footer_size = sizeof(CowFooter);
+ header_.op_size = sizeof(CowOperation);
header_.block_size = options_.block_size;
header_.num_merge_ops = 0;
+ header_.cluster_ops = options_.cluster_ops;
footer_ = {};
footer_.op.data_length = 64;
footer_.op.type = kCowFooterOp;
@@ -108,6 +110,10 @@
LOG(ERROR) << "unrecognized compression: " << options_.compression;
return false;
}
+ if (options_.cluster_ops == 1) {
+ LOG(ERROR) << "Clusters must contain at least two operations to function.";
+ return false;
+ }
return true;
}
@@ -165,6 +171,19 @@
return OpenForAppend(label);
}
+void CowWriter::InitPos() {
+ next_op_pos_ = sizeof(header_);
+ cluster_size_ = header_.cluster_ops * sizeof(CowOperation);
+ if (header_.cluster_ops) {
+ next_data_pos_ = next_op_pos_ + cluster_size_;
+ } else {
+ next_data_pos_ = next_op_pos_ + sizeof(CowOperation);
+ }
+ ops_.clear();
+ current_cluster_size_ = 0;
+ current_data_size_ = 0;
+}
+
bool CowWriter::OpenForWrite() {
// This limitation is tied to the data field size in CowOperation.
if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
@@ -184,7 +203,7 @@
return false;
}
- next_op_pos_ = sizeof(header_);
+ InitPos();
return true;
}
@@ -197,13 +216,14 @@
}
options_.block_size = header_.block_size;
+ options_.cluster_ops = header_.cluster_ops;
// Reset this, since we're going to reimport all operations.
footer_.op.num_ops = 0;
- next_op_pos_ = sizeof(header_);
- ops_.resize(0);
+ InitPos();
auto iter = reader->GetOpIter();
+
while (!iter->Done()) {
AddOperation(iter->Get());
iter->Next();
@@ -234,14 +254,12 @@
bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
- uint64_t pos;
CHECK(!merge_in_progress_);
for (size_t i = 0; i < size / header_.block_size; i++) {
CowOperation op = {};
op.type = kCowReplaceOp;
op.new_block = new_block_start + i;
- GetDataPos(&pos);
- op.source = pos + sizeof(op);
+ op.source = next_data_pos_;
if (compression_) {
auto data = Compress(iter, header_.block_size);
@@ -293,6 +311,14 @@
return WriteOperation(op) && Sync();
}
+bool CowWriter::EmitCluster() {
+ CowOperation op = {};
+ op.type = kCowClusterOp;
+ // Next cluster starts after remainder of current cluster and the next data block.
+ op.source = current_data_size_ + cluster_size_ - current_cluster_size_ - sizeof(CowOperation);
+ return WriteOperation(op);
+}
+
std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
switch (compression_) {
case kCowCompressGz: {
@@ -345,11 +371,23 @@
}
bool CowWriter::Finalize() {
- footer_.op.ops_size = ops_.size();
- uint64_t pos;
+ auto continue_cluster_size = current_cluster_size_;
+ auto continue_data_size = current_data_size_;
+ auto continue_data_pos = next_data_pos_;
+ auto continue_op_pos = next_op_pos_;
+ auto continue_size = ops_.size();
+ bool extra_cluster = false;
- if (!GetDataPos(&pos)) {
- PLOG(ERROR) << "failed to get file position";
+ // Footer should be at the end of a file, so if there is data after the current block, end it
+ // and start a new cluster.
+ if (cluster_size_ && current_data_size_ > 0) {
+ EmitCluster();
+ extra_cluster = true;
+ }
+
+ footer_.op.ops_size = ops_.size();
+ if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
+ PLOG(ERROR) << "Failed to seek to footer position.";
return false;
}
memset(&footer_.data.ops_checksum, 0, sizeof(uint8_t) * 32);
@@ -364,16 +402,24 @@
return false;
}
- // Re-position for any subsequent writes.
- if (lseek(fd_.get(), pos, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek ops failed";
- return false;
+ // Reposition for additional Writing
+ if (extra_cluster) {
+ current_cluster_size_ = continue_cluster_size;
+ current_data_size_ = continue_data_size;
+ next_data_pos_ = continue_data_pos;
+ next_op_pos_ = continue_op_pos;
+ ops_.resize(continue_size);
}
+
return Sync();
}
uint64_t CowWriter::GetCowSize() {
- return next_op_pos_ + sizeof(footer_);
+ if (current_data_size_ > 0) {
+ return next_data_pos_ + sizeof(footer_);
+ } else {
+ return next_op_pos_ + sizeof(footer_);
+ }
}
bool CowWriter::GetDataPos(uint64_t* pos) {
@@ -387,6 +433,15 @@
}
bool CowWriter::WriteOperation(const CowOperation& op, const void* data, size_t size) {
+ // If there isn't room for this op and the cluster end op, end the current cluster
+ if (cluster_size_ && op.type != kCowClusterOp &&
+ cluster_size_ < current_cluster_size_ + 2 * sizeof(op)) {
+ if (!EmitCluster()) return false;
+ }
+ if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed for writing operation.";
+ return false;
+ }
if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&op), sizeof(op))) {
return false;
}
@@ -399,11 +454,26 @@
void CowWriter::AddOperation(const CowOperation& op) {
footer_.op.num_ops++;
- next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op);
+
+ if (op.type == kCowClusterOp) {
+ current_cluster_size_ = 0;
+ current_data_size_ = 0;
+ } else if (header_.cluster_ops) {
+ current_cluster_size_ += sizeof(op);
+ current_data_size_ += op.data_length;
+ }
+
+ next_data_pos_ += op.data_length + GetNextDataOffset(op, header_.cluster_ops);
+ next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op, header_.cluster_ops);
ops_.insert(ops_.size(), reinterpret_cast<const uint8_t*>(&op), sizeof(op));
}
bool CowWriter::WriteRawData(const void* data, size_t size) {
+ if (lseek(fd_.get(), next_data_pos_, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed for writing data.";
+ return false;
+ }
+
if (!android::base::WriteFully(fd_, data, size)) {
return false;
}
@@ -421,7 +491,7 @@
return true;
}
-bool CowWriter::CommitMerge(int merged_ops) {
+bool CowWriter::CommitMerge(int merged_ops, bool sync) {
CHECK(merge_in_progress_);
header_.num_merge_ops += merged_ops;
@@ -436,7 +506,11 @@
return false;
}
- return Sync();
+ // Sync only for merging of copy operations.
+ if (sync) {
+ return Sync();
+ }
+ return true;
}
bool CowWriter::Truncate(off_t length) {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 80766ff..797b8ef 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -57,9 +57,15 @@
// Size of footer struct
uint16_t footer_size;
+ // Size of op struct
+ uint16_t op_size;
+
// The size of block operations, in bytes.
uint32_t block_size;
+ // The number of ops to cluster together. 0 For no clustering. Cannot be 1.
+ uint32_t cluster_ops;
+
// Tracks merge operations completed
uint64_t num_merge_ops;
} __attribute__((packed));
@@ -113,13 +119,15 @@
// For copy operations, this is a block location in the source image.
//
// For replace operations, this is a byte offset within the COW's data
- // section (eg, not landing within the header or metadata). It is an
+ // sections (eg, not landing within the header or metadata). It is an
// absolute position within the image.
//
// For zero operations (replace with all zeroes), this is unused and must
// be zero.
//
// For Label operations, this is the value of the applied label.
+ //
+ // For Cluster operations, this is the length of the following data region
uint64_t source;
} __attribute__((packed));
@@ -129,6 +137,7 @@
static constexpr uint8_t kCowReplaceOp = 2;
static constexpr uint8_t kCowZeroOp = 3;
static constexpr uint8_t kCowLabelOp = 4;
+static constexpr uint8_t kCowClusterOp = 5;
static constexpr uint8_t kCowFooterOp = -1;
static constexpr uint8_t kCowCompressNone = 0;
@@ -142,7 +151,10 @@
std::ostream& operator<<(std::ostream& os, CowOperation const& arg);
-int64_t GetNextOpOffset(const CowOperation& op);
+int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_size);
+int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_size);
+
+bool IsMetadataOp(const CowOperation& op);
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index be69225..62b54f9 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -140,6 +140,8 @@
void UpdateMergeProgress(uint64_t merge_ops) { header_.num_merge_ops += merge_ops; }
+ void InitializeMerge();
+
private:
bool ParseOps(std::optional<uint64_t> label);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index e9320b0..fd43cce 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -33,6 +33,9 @@
// Maximum number of blocks that can be written.
std::optional<uint64_t> max_blocks;
+
+ // Number of CowOperations in a cluster. 0 for no clustering. Cannot be 1.
+ uint32_t cluster_ops = 0;
};
// Interface for writing to a snapuserd COW. All operations are ordered; merges
@@ -98,7 +101,7 @@
bool InitializeAppend(android::base::borrowed_fd fd, uint64_t label);
void InitializeMerge(android::base::borrowed_fd fd, CowHeader* header);
- bool CommitMerge(int merged_ops);
+ bool CommitMerge(int merged_ops, bool sync);
bool Finalize() override;
@@ -111,6 +114,7 @@
virtual bool EmitLabel(uint64_t label) override;
private:
+ bool EmitCluster();
void SetupHeaders();
bool ParseOptions();
bool OpenForWrite();
@@ -120,6 +124,7 @@
bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0);
void AddOperation(const CowOperation& op);
std::basic_string<uint8_t> Compress(const void* data, size_t length);
+ void InitPos();
bool SetFd(android::base::borrowed_fd fd);
bool Sync();
@@ -132,6 +137,10 @@
CowFooter footer_{};
int compression_ = 0;
uint64_t next_op_pos_ = 0;
+ uint64_t next_data_pos_ = 0;
+ uint32_t cluster_size_ = 0;
+ uint32_t current_cluster_size_ = 0;
+ uint64_t current_data_size_ = 0;
bool is_dev_null_ = false;
bool merge_in_progress_ = false;
bool is_block_device_ = false;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
index 24b44fa..eec6d45 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
@@ -81,7 +81,7 @@
bool ReadDiskExceptions(chunk_t chunk, size_t size);
bool ReadData(chunk_t chunk, size_t size);
bool IsChunkIdMetadata(chunk_t chunk);
- chunk_t GetNextAllocatableChunkId(chunk_t chunk);
+ chunk_t GetNextAllocatableChunkId(chunk_t chunk_id);
bool ProcessReplaceOp(const CowOperation* cow_op);
bool ProcessCopyOp(const CowOperation* cow_op);
@@ -90,8 +90,7 @@
loff_t GetMergeStartOffset(void* merged_buffer, void* unmerged_buffer,
int* unmerged_exceptions);
int GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
- int unmerged_exceptions);
- bool AdvanceMergedOps(int merged_ops_cur_iter);
+ int unmerged_exceptions, bool* copy_op);
bool ProcessMergeComplete(chunk_t chunk, void* buffer);
sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/inspect_cow.cpp
index 6046bad..5ad61f3 100644
--- a/fs_mgr/libsnapshot/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/inspect_cow.cpp
@@ -14,6 +14,7 @@
// limitations under the License.
//
#include <stdio.h>
+#include <unistd.h>
#include <iostream>
#include <string>
@@ -34,7 +35,11 @@
}
}
-static bool Inspect(const std::string& path) {
+static void usage(void) {
+ LOG(ERROR) << "Usage: inspect_cow [-s] <COW_FILE>";
+}
+
+static bool Inspect(const std::string& path, bool silent) {
android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
if (fd < 0) {
PLOG(ERROR) << "open failed: " << path;
@@ -52,19 +57,29 @@
LOG(ERROR) << "could not get header: " << path;
return false;
}
+ CowFooter footer;
+ bool has_footer = false;
+ if (reader.GetFooter(&footer)) has_footer = true;
- std::cout << "Major version: " << header.major_version << "\n";
- std::cout << "Minor version: " << header.minor_version << "\n";
- std::cout << "Header size: " << header.header_size << "\n";
- std::cout << "Footer size: " << header.footer_size << "\n";
- std::cout << "Block size: " << header.block_size << "\n";
- std::cout << "\n";
+ if (!silent) {
+ std::cout << "Major version: " << header.major_version << "\n";
+ std::cout << "Minor version: " << header.minor_version << "\n";
+ std::cout << "Header size: " << header.header_size << "\n";
+ std::cout << "Footer size: " << header.footer_size << "\n";
+ std::cout << "Block size: " << header.block_size << "\n";
+ std::cout << "\n";
+ if (has_footer) {
+ std::cout << "Total Ops size: " << footer.op.ops_size << "\n";
+ std::cout << "Number of Ops: " << footer.op.num_ops << "\n";
+ std::cout << "\n";
+ }
+ }
auto iter = reader.GetOpIter();
while (!iter->Done()) {
const CowOperation& op = iter->Get();
- std::cout << op << "\n";
+ if (!silent) std::cout << op << "\n";
iter->Next();
}
@@ -76,14 +91,25 @@
} // namespace android
int main(int argc, char** argv) {
+ int ch;
+ bool silent = false;
+ while ((ch = getopt(argc, argv, "s")) != -1) {
+ switch (ch) {
+ case 's':
+ silent = true;
+ break;
+ default:
+ android::snapshot::usage();
+ }
+ }
android::base::InitLogging(argv, android::snapshot::MyLogger);
- if (argc < 2) {
- LOG(ERROR) << "Usage: inspect_cow <COW_FILE>";
+ if (argc < optind + 1) {
+ android::snapshot::usage();
return 1;
}
- if (!android::snapshot::Inspect(argv[1])) {
+ if (!android::snapshot::Inspect(argv[optind], silent)) {
return 1;
}
return 0;
diff --git a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
index d0b8f52..6a5754d 100644
--- a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
+++ b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
@@ -53,6 +53,7 @@
DEFINE_string(source_tf, "", "Source target files (dir or zip file) for incremental payloads");
DEFINE_string(compression, "gz", "Compression type to use (none or gz)");
+DEFINE_uint32(cluster_ops, 0, "Number of Cow Ops per cluster (0 or >1)");
void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
unsigned int, const char* message) {
@@ -189,6 +190,7 @@
CowOptions options;
options.block_size = kBlockSize;
options.compression = FLAGS_compression;
+ options.cluster_ops = FLAGS_cluster_ops;
writer_ = std::make_unique<CowWriter>(options);
if (!writer_->Initialize(std::move(fd))) {
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
index b56d879..5ee8e25 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader.cpp
@@ -90,7 +90,7 @@
op_iter_ = cow_->GetOpIter();
while (!op_iter_->Done()) {
const CowOperation* op = &op_iter_->Get();
- if (op->type == kCowLabelOp || op->type == kCowFooterOp) {
+ if (IsMetadataOp(*op)) {
op_iter_->Next();
continue;
}
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index 49e6c3d..34254a3 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -31,7 +31,7 @@
#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
-static constexpr size_t PAYLOAD_SIZE = (1UL << 16);
+static constexpr size_t PAYLOAD_SIZE = (1UL << 20);
static_assert(PAYLOAD_SIZE >= BLOCK_SIZE);
@@ -156,11 +156,11 @@
size_t read_size = size;
bool ret = true;
chunk_t chunk_key = chunk;
- uint32_t stride;
- lldiv_t divresult;
- // Size should always be aligned
- CHECK((read_size & (BLOCK_SIZE - 1)) == 0);
+ if (!((read_size & (BLOCK_SIZE - 1)) == 0)) {
+ SNAP_LOG(ERROR) << "ReadData - unaligned read_size: " << read_size;
+ return false;
+ }
while (read_size > 0) {
const CowOperation* cow_op = chunk_map_[chunk_key];
@@ -204,24 +204,8 @@
// are contiguous
chunk_key += 1;
- if (cow_op->type == kCowCopyOp) CHECK(read_size == 0);
-
- // This is similar to the way when chunk IDs were assigned
- // in ReadMetadata().
- //
- // Skip if the chunk id represents a metadata chunk.
- stride = exceptions_per_area_ + 1;
- divresult = lldiv(chunk_key, stride);
- if (divresult.rem == NUM_SNAPSHOT_HDR_CHUNKS) {
- // Crossing exception boundary. Kernel will never
- // issue IO which is spanning between a data chunk
- // and a metadata chunk. This should be perfectly aligned.
- //
- // Since the input read_size is 4k aligned, we will
- // always end up reading all 256 data chunks in one area.
- // Thus, every multiple of 4K IO represents 256 data chunks
+ if (cow_op->type == kCowCopyOp) {
CHECK(read_size == 0);
- break;
}
}
@@ -330,7 +314,7 @@
}
int Snapuserd::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
- int unmerged_exceptions) {
+ int unmerged_exceptions, bool* copy_op) {
int merged_ops_cur_iter = 0;
// Find the operations which are merged in this cycle.
@@ -346,6 +330,12 @@
if (cow_de->new_chunk != 0) {
merged_ops_cur_iter += 1;
offset += sizeof(struct disk_exception);
+ const CowOperation* cow_op = chunk_map_[cow_de->new_chunk];
+ CHECK(cow_op != nullptr);
+ CHECK(cow_op->new_block == cow_de->old_chunk);
+ if (cow_op->type == kCowCopyOp) {
+ *copy_op = true;
+ }
// zero out to indicate that operation is merged.
cow_de->old_chunk = 0;
cow_de->new_chunk = 0;
@@ -367,44 +357,12 @@
}
}
+ if (*copy_op) {
+ CHECK(merged_ops_cur_iter == 1);
+ }
return merged_ops_cur_iter;
}
-bool Snapuserd::AdvanceMergedOps(int merged_ops_cur_iter) {
- // Advance the merge operation pointer in the
- // vector.
- //
- // cowop_iter_ is already initialized in ReadMetadata(). Just resume the
- // merge process
- while (!cowop_iter_->Done() && merged_ops_cur_iter) {
- const CowOperation* cow_op = &cowop_iter_->Get();
- CHECK(cow_op != nullptr);
-
- if (cow_op->type == kCowFooterOp || cow_op->type == kCowLabelOp) {
- cowop_iter_->Next();
- continue;
- }
-
- if (!(cow_op->type == kCowReplaceOp || cow_op->type == kCowZeroOp ||
- cow_op->type == kCowCopyOp)) {
- SNAP_LOG(ERROR) << "Unknown operation-type found during merge: " << cow_op->type;
- return false;
- }
-
- merged_ops_cur_iter -= 1;
- SNAP_LOG(DEBUG) << "Merge op found of type " << cow_op->type
- << "Pending-merge-ops: " << merged_ops_cur_iter;
- cowop_iter_->Next();
- }
-
- if (cowop_iter_->Done()) {
- CHECK(merged_ops_cur_iter == 0);
- SNAP_LOG(DEBUG) << "All cow operations merged successfully in this cycle";
- }
-
- return true;
-}
-
bool Snapuserd::ProcessMergeComplete(chunk_t chunk, void* buffer) {
uint32_t stride = exceptions_per_area_ + 1;
CowHeader header;
@@ -423,21 +381,47 @@
int unmerged_exceptions = 0;
loff_t offset = GetMergeStartOffset(buffer, vec_[divresult.quot].get(), &unmerged_exceptions);
- int merged_ops_cur_iter =
- GetNumberOfMergedOps(buffer, vec_[divresult.quot].get(), offset, unmerged_exceptions);
+ bool copy_op = false;
+ // Check if the merged operation is a copy operation. If so, then we need
+ // to explicitly sync the metadata before initiating the next merge.
+ // For ex: Consider a following sequence of copy operations in the COW file:
+ //
+ // Op-1: Copy 2 -> 3
+ // Op-2: Copy 1 -> 2
+ // Op-3: Copy 5 -> 10
+ //
+ // Op-1 and Op-2 are overlapping copy operations. The merge sequence will
+ // look like:
+ //
+ // Merge op-1: Copy 2 -> 3
+ // Merge op-2: Copy 1 -> 2
+ // Merge op-3: Copy 5 -> 10
+ //
+ // Now, let's say we have a crash _after_ Merge op-2; Block 2 contents would
+ // have been over-written by Block-1 after merge op-2. During next reboot,
+ // kernel will request the metadata for all the un-merged blocks. If we had
+ // not sync the metadata after Merge-op 1 and Merge op-2, snapuser daemon
+ // will think that these merge operations are still pending and hence will
+ // inform the kernel that Op-1 and Op-2 are un-merged blocks. When kernel
+ // resumes back the merging process, it will attempt to redo the Merge op-1
+ // once again. However, block 2 contents are wrong as it has the contents
+ // of block 1 from previous merge cycle. Although, merge will silently succeed,
+ // this will lead to silent data corruption.
+ //
+ int merged_ops_cur_iter = GetNumberOfMergedOps(buffer, vec_[divresult.quot].get(), offset,
+ unmerged_exceptions, ©_op);
// There should be at least one operation merged in this cycle
CHECK(merged_ops_cur_iter > 0);
- if (!AdvanceMergedOps(merged_ops_cur_iter)) return false;
header.num_merge_ops += merged_ops_cur_iter;
reader_->UpdateMergeProgress(merged_ops_cur_iter);
- if (!writer_->CommitMerge(merged_ops_cur_iter)) {
+ if (!writer_->CommitMerge(merged_ops_cur_iter, copy_op)) {
SNAP_LOG(ERROR) << "CommitMerge failed...";
return false;
}
- SNAP_LOG(DEBUG) << "Merge success";
+ SNAP_LOG(DEBUG) << "Merge success: " << merged_ops_cur_iter << "chunk: " << chunk;
return true;
}
@@ -532,6 +516,7 @@
CHECK(header.block_size == BLOCK_SIZE);
SNAP_LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
+ reader_->InitializeMerge();
writer_ = std::make_unique<CowWriter>(options);
writer_->InitializeMerge(cow_fd_.get(), &header);
@@ -543,7 +528,8 @@
// Start from chunk number 2. Chunk 0 represents header and chunk 1
// represents first metadata page.
- chunk_t next_free = NUM_SNAPSHOT_HDR_CHUNKS + 1;
+ chunk_t data_chunk_id = NUM_SNAPSHOT_HDR_CHUNKS + 1;
+ size_t num_ops = 0;
loff_t offset = 0;
std::unique_ptr<uint8_t[]> de_ptr =
@@ -553,43 +539,34 @@
// is 0. When Area is not filled completely with all 256 exceptions,
// this memset will ensure that metadata read is completed.
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
- size_t num_ops = 0;
while (!cowop_riter_->Done()) {
const CowOperation* cow_op = &cowop_riter_->Get();
struct disk_exception* de =
reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
- if (cow_op->type == kCowFooterOp || cow_op->type == kCowLabelOp) {
+ if (IsMetadataOp(*cow_op)) {
cowop_riter_->Next();
continue;
}
- if (!(cow_op->type == kCowReplaceOp || cow_op->type == kCowZeroOp ||
- cow_op->type == kCowCopyOp)) {
- SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
- return false;
- }
-
metadata_found = true;
if ((cow_op->type == kCowCopyOp || prev_copy_op)) {
- next_free = GetNextAllocatableChunkId(next_free);
+ data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
}
prev_copy_op = (cow_op->type == kCowCopyOp);
// Construct the disk-exception
de->old_chunk = cow_op->new_block;
- de->new_chunk = next_free;
+ de->new_chunk = data_chunk_id;
SNAP_LOG(DEBUG) << "Old-chunk: " << de->old_chunk << "New-chunk: " << de->new_chunk;
// Store operation pointer.
- chunk_map_[next_free] = cow_op;
+ chunk_map_[data_chunk_id] = cow_op;
num_ops += 1;
-
offset += sizeof(struct disk_exception);
-
cowop_riter_->Next();
if (num_ops == exceptions_per_area_) {
@@ -610,7 +587,7 @@
}
}
- next_free = GetNextAllocatableChunkId(next_free);
+ data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
}
// Partially filled area or there is no metadata
@@ -622,14 +599,11 @@
<< "Areas : " << vec_.size();
}
- SNAP_LOG(DEBUG) << "ReadMetadata() completed. chunk_id: " << next_free
- << "Num Sector: " << ChunkToSector(next_free);
-
- // Initialize the iterator for merging
- cowop_iter_ = reader_->GetOpIter();
+ SNAP_LOG(DEBUG) << "ReadMetadata() completed. Final_chunk_id: " << data_chunk_id
+ << "Num Sector: " << ChunkToSector(data_chunk_id);
// Total number of sectors required for creating dm-user device
- num_sectors_ = ChunkToSector(next_free);
+ num_sectors_ = ChunkToSector(data_chunk_id);
metadata_read_done_ = true;
return true;
}
@@ -759,6 +733,8 @@
<< "Sector: " << header->sector;
}
} else {
+ SNAP_LOG(DEBUG) << "ReadData: chunk: " << chunk << " len: " << header->len
+ << " read_size: " << read_size << " offset: " << offset;
chunk_t num_chunks_read = (offset >> BLOCK_SHIFT);
if (!ReadData(chunk + num_chunks_read, read_size)) {
SNAP_LOG(ERROR) << "ReadData failed for chunk id: " << chunk
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd_client.cpp
index 7282bff..16d02e4 100644
--- a/fs_mgr/libsnapshot/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_client.cpp
@@ -57,7 +57,7 @@
SnapuserdClient::SnapuserdClient(android::base::unique_fd&& sockfd) : sockfd_(std::move(sockfd)) {}
static inline bool IsRetryErrno() {
- return errno == ECONNREFUSED || errno == EINTR;
+ return errno == ECONNREFUSED || errno == EINTR || errno == ENOENT;
}
std::unique_ptr<SnapuserdClient> SnapuserdClient::Connect(const std::string& socket_name,
@@ -112,6 +112,7 @@
}
bool SnapuserdClient::Sendmsg(const std::string& msg) {
+ LOG(DEBUG) << "Sendmsg: msg " << msg << " sockfd: " << sockfd_;
ssize_t numBytesSent = TEMP_FAILURE_RETRY(send(sockfd_, msg.data(), msg.size(), 0));
if (numBytesSent < 0) {
PLOG(ERROR) << "Send failed";
diff --git a/init/builtins.cpp b/init/builtins.cpp
index b235d2f..c44e03e 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1232,7 +1232,7 @@
}
static Result<void> GenerateLinkerConfiguration() {
- const char* linkerconfig_binary = "/system/bin/linkerconfig";
+ const char* linkerconfig_binary = "/apex/com.android.runtime/bin/linkerconfig";
const char* linkerconfig_target = "/linkerconfig";
const char* arguments[] = {linkerconfig_binary, "--target", linkerconfig_target};
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 284c0b9..cf809f1 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -32,6 +32,7 @@
cc_library_headers {
name: "libcutils_headers",
vendor_available: true,
+ product_available: true,
recovery_available: true,
ramdisk_available: true,
vendor_ramdisk_available: true,
@@ -45,7 +46,10 @@
export_include_dirs: ["include"],
target: {
vendor: {
- override_export_include_dirs: ["include_vndk"],
+ override_export_include_dirs: ["include_outside_system"],
+ },
+ product: {
+ override_export_include_dirs: ["include_outside_system"],
},
linux_bionic: {
enabled: true,
@@ -60,6 +64,7 @@
cc_library {
name: "libcutils_sockets",
vendor_available: true,
+ product_available: true,
recovery_available: true,
ramdisk_available: true,
vendor_ramdisk_available: true,
@@ -143,6 +148,7 @@
cc_library {
name: "libcutils",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -234,14 +240,19 @@
},
},
+ // qtaguid.cpp loads libnetd_client.so with dlopen(). Since
+ // the interface of libnetd_client.so may vary between AOSP
+ // releases, exclude qtaguid.cpp from the VNDK-SP variant.
vendor: {
exclude_srcs: [
- // qtaguid.cpp loads libnetd_client.so with dlopen(). Since
- // the interface of libnetd_client.so may vary between AOSP
- // releases, exclude qtaguid.cpp from the VNDK-SP variant.
"qtaguid.cpp",
],
- }
+ },
+ product: {
+ exclude_srcs: [
+ "qtaguid.cpp",
+ ],
+ },
},
whole_static_libs: ["libcutils_sockets"],
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
index ba4846e..bbbba6d 100644
--- a/libcutils/include/cutils/threads.h
+++ b/libcutils/include/cutils/threads.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBS_CUTILS_THREADS_H
-#define _LIBS_CUTILS_THREADS_H
+#pragma once
#include <sys/types.h>
@@ -29,16 +28,6 @@
extern "C" {
#endif
-//
-// Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
-//
-
-extern pid_t gettid();
-
-//
-// Deprecated: use `_Thread_local` in C or `thread_local` in C++.
-//
-
#if !defined(_WIN32)
typedef struct {
@@ -49,29 +38,24 @@
#define THREAD_STORE_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0 }
-#else // !defined(_WIN32)
+#endif
-typedef struct {
- int lock_init;
- int has_tls;
- DWORD tls;
- CRITICAL_SECTION lock;
-} thread_store_t;
+//
+// Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
+//
+extern pid_t gettid();
-#define THREAD_STORE_INITIALIZER { 0, 0, 0, {0, 0, 0, 0, 0, 0} }
-
-#endif // !defined(_WIN32)
-
-typedef void (*thread_store_destruct_t)(void* value);
-
-extern void* thread_store_get(thread_store_t* store);
-
-extern void thread_store_set(thread_store_t* store,
- void* value,
- thread_store_destruct_t destroy);
+//
+// Deprecated: use `_Thread_local` in C or `thread_local` in C++.
+//
+#if !defined(_WIN32)
+typedef void (*thread_store_destruct_t)(void* x);
+extern void* thread_store_get(thread_store_t* x)
+ __attribute__((__deprecated__("use thread_local instead")));
+extern void thread_store_set(thread_store_t* x, void* y, thread_store_destruct_t z)
+ __attribute__((__deprecated__("use thread_local instead")));
+#endif
#ifdef __cplusplus
}
#endif
-
-#endif /* _LIBS_CUTILS_THREADS_H */
diff --git a/libcutils/include_vndk/cutils/android_filesystem_config.h b/libcutils/include_outside_system/cutils/android_filesystem_config.h
similarity index 100%
rename from libcutils/include_vndk/cutils/android_filesystem_config.h
rename to libcutils/include_outside_system/cutils/android_filesystem_config.h
diff --git a/libcutils/include_vndk/cutils/android_get_control_file.h b/libcutils/include_outside_system/cutils/android_get_control_file.h
similarity index 100%
rename from libcutils/include_vndk/cutils/android_get_control_file.h
rename to libcutils/include_outside_system/cutils/android_get_control_file.h
diff --git a/libcutils/include_vndk/cutils/android_reboot.h b/libcutils/include_outside_system/cutils/android_reboot.h
similarity index 100%
rename from libcutils/include_vndk/cutils/android_reboot.h
rename to libcutils/include_outside_system/cutils/android_reboot.h
diff --git a/libcutils/include_vndk/cutils/ashmem.h b/libcutils/include_outside_system/cutils/ashmem.h
similarity index 100%
rename from libcutils/include_vndk/cutils/ashmem.h
rename to libcutils/include_outside_system/cutils/ashmem.h
diff --git a/libcutils/include_vndk/cutils/atomic.h b/libcutils/include_outside_system/cutils/atomic.h
similarity index 100%
rename from libcutils/include_vndk/cutils/atomic.h
rename to libcutils/include_outside_system/cutils/atomic.h
diff --git a/libcutils/include_vndk/cutils/bitops.h b/libcutils/include_outside_system/cutils/bitops.h
similarity index 100%
rename from libcutils/include_vndk/cutils/bitops.h
rename to libcutils/include_outside_system/cutils/bitops.h
diff --git a/libcutils/include_vndk/cutils/compiler.h b/libcutils/include_outside_system/cutils/compiler.h
similarity index 100%
rename from libcutils/include_vndk/cutils/compiler.h
rename to libcutils/include_outside_system/cutils/compiler.h
diff --git a/libcutils/include_vndk/cutils/config_utils.h b/libcutils/include_outside_system/cutils/config_utils.h
similarity index 100%
rename from libcutils/include_vndk/cutils/config_utils.h
rename to libcutils/include_outside_system/cutils/config_utils.h
diff --git a/libcutils/include_vndk/cutils/fs.h b/libcutils/include_outside_system/cutils/fs.h
similarity index 100%
rename from libcutils/include_vndk/cutils/fs.h
rename to libcutils/include_outside_system/cutils/fs.h
diff --git a/libcutils/include_vndk/cutils/hashmap.h b/libcutils/include_outside_system/cutils/hashmap.h
similarity index 100%
rename from libcutils/include_vndk/cutils/hashmap.h
rename to libcutils/include_outside_system/cutils/hashmap.h
diff --git a/libcutils/include_vndk/cutils/iosched_policy.h b/libcutils/include_outside_system/cutils/iosched_policy.h
similarity index 100%
rename from libcutils/include_vndk/cutils/iosched_policy.h
rename to libcutils/include_outside_system/cutils/iosched_policy.h
diff --git a/libcutils/include_vndk/cutils/klog.h b/libcutils/include_outside_system/cutils/klog.h
similarity index 100%
rename from libcutils/include_vndk/cutils/klog.h
rename to libcutils/include_outside_system/cutils/klog.h
diff --git a/libcutils/include_vndk/cutils/list.h b/libcutils/include_outside_system/cutils/list.h
similarity index 100%
rename from libcutils/include_vndk/cutils/list.h
rename to libcutils/include_outside_system/cutils/list.h
diff --git a/libcutils/include_vndk/cutils/log.h b/libcutils/include_outside_system/cutils/log.h
similarity index 100%
rename from libcutils/include_vndk/cutils/log.h
rename to libcutils/include_outside_system/cutils/log.h
diff --git a/libcutils/include_vndk/cutils/memory.h b/libcutils/include_outside_system/cutils/memory.h
similarity index 100%
rename from libcutils/include_vndk/cutils/memory.h
rename to libcutils/include_outside_system/cutils/memory.h
diff --git a/libcutils/include_vndk/cutils/misc.h b/libcutils/include_outside_system/cutils/misc.h
similarity index 100%
rename from libcutils/include_vndk/cutils/misc.h
rename to libcutils/include_outside_system/cutils/misc.h
diff --git a/libcutils/include_vndk/cutils/multiuser.h b/libcutils/include_outside_system/cutils/multiuser.h
similarity index 100%
rename from libcutils/include_vndk/cutils/multiuser.h
rename to libcutils/include_outside_system/cutils/multiuser.h
diff --git a/libcutils/include_vndk/cutils/native_handle.h b/libcutils/include_outside_system/cutils/native_handle.h
similarity index 100%
rename from libcutils/include_vndk/cutils/native_handle.h
rename to libcutils/include_outside_system/cutils/native_handle.h
diff --git a/libcutils/include_vndk/cutils/partition_utils.h b/libcutils/include_outside_system/cutils/partition_utils.h
similarity index 100%
rename from libcutils/include_vndk/cutils/partition_utils.h
rename to libcutils/include_outside_system/cutils/partition_utils.h
diff --git a/libcutils/include_vndk/cutils/properties.h b/libcutils/include_outside_system/cutils/properties.h
similarity index 100%
rename from libcutils/include_vndk/cutils/properties.h
rename to libcutils/include_outside_system/cutils/properties.h
diff --git a/libcutils/include_vndk/cutils/qtaguid.h b/libcutils/include_outside_system/cutils/qtaguid.h
similarity index 100%
rename from libcutils/include_vndk/cutils/qtaguid.h
rename to libcutils/include_outside_system/cutils/qtaguid.h
diff --git a/libcutils/include_vndk/cutils/record_stream.h b/libcutils/include_outside_system/cutils/record_stream.h
similarity index 100%
rename from libcutils/include_vndk/cutils/record_stream.h
rename to libcutils/include_outside_system/cutils/record_stream.h
diff --git a/libcutils/include_vndk/cutils/sched_policy.h b/libcutils/include_outside_system/cutils/sched_policy.h
similarity index 100%
rename from libcutils/include_vndk/cutils/sched_policy.h
rename to libcutils/include_outside_system/cutils/sched_policy.h
diff --git a/libcutils/include_vndk/cutils/sockets.h b/libcutils/include_outside_system/cutils/sockets.h
similarity index 100%
rename from libcutils/include_vndk/cutils/sockets.h
rename to libcutils/include_outside_system/cutils/sockets.h
diff --git a/libcutils/include_vndk/cutils/str_parms.h b/libcutils/include_outside_system/cutils/str_parms.h
similarity index 100%
rename from libcutils/include_vndk/cutils/str_parms.h
rename to libcutils/include_outside_system/cutils/str_parms.h
diff --git a/libcutils/include_vndk/cutils/threads.h b/libcutils/include_outside_system/cutils/threads.h
similarity index 100%
rename from libcutils/include_vndk/cutils/threads.h
rename to libcutils/include_outside_system/cutils/threads.h
diff --git a/libcutils/include_vndk/cutils/trace.h b/libcutils/include_outside_system/cutils/trace.h
similarity index 100%
rename from libcutils/include_vndk/cutils/trace.h
rename to libcutils/include_outside_system/cutils/trace.h
diff --git a/libcutils/include_vndk/cutils/uevent.h b/libcutils/include_outside_system/cutils/uevent.h
similarity index 100%
rename from libcutils/include_vndk/cutils/uevent.h
rename to libcutils/include_outside_system/cutils/uevent.h
diff --git a/libcutils/threads.cpp b/libcutils/threads.cpp
index a7e6b2d..eac63b5 100644
--- a/libcutils/threads.cpp
+++ b/libcutils/threads.cpp
@@ -47,7 +47,6 @@
#endif // __ANDROID__
#if !defined(_WIN32)
-
void* thread_store_get( thread_store_t* store )
{
if (!store->has_tls)
@@ -72,40 +71,4 @@
pthread_setspecific( store->tls, value );
}
-
-#else /* !defined(_WIN32) */
-void* thread_store_get( thread_store_t* store )
-{
- if (!store->has_tls)
- return NULL;
-
- return (void*) TlsGetValue( store->tls );
-}
-
-void thread_store_set( thread_store_t* store,
- void* value,
- thread_store_destruct_t /*destroy*/ )
-{
- /* XXX: can't use destructor on thread exit */
- if (!store->lock_init) {
- store->lock_init = -1;
- InitializeCriticalSection( &store->lock );
- store->lock_init = -2;
- } else while (store->lock_init != -2) {
- Sleep(10); /* 10ms */
- }
-
- EnterCriticalSection( &store->lock );
- if (!store->has_tls) {
- store->tls = TlsAlloc();
- if (store->tls == TLS_OUT_OF_INDEXES) {
- LeaveCriticalSection( &store->lock );
- return;
- }
- store->has_tls = 1;
- }
- LeaveCriticalSection( &store->lock );
-
- TlsSetValue( store->tls, value );
-}
-#endif /* !defined(_WIN32) */
+#endif
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 71e2b91..f104100 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -1,6 +1,7 @@
cc_library_headers {
name: "libprocessgroup_headers",
vendor_available: true,
+ product_available: true,
ramdisk_available: true,
vendor_ramdisk_available: true,
recovery_available: true,
@@ -36,6 +37,7 @@
vendor_ramdisk_available: true,
recovery_available: true,
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
diff --git a/libprocessgroup/include/processgroup/sched_policy.h b/libprocessgroup/include/processgroup/sched_policy.h
index 945d90c..a18847e 100644
--- a/libprocessgroup/include/processgroup/sched_policy.h
+++ b/libprocessgroup/include/processgroup/sched_policy.h
@@ -42,7 +42,7 @@
SP_DEFAULT = -1,
SP_BACKGROUND = 0,
SP_FOREGROUND = 1,
- SP_SYSTEM = 2, // can't be used with set_sched_policy()
+ SP_SYSTEM = 2,
SP_AUDIO_APP = 3,
SP_AUDIO_SYS = 4,
SP_TOP_APP = 5,
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 5b7a28a..792af6f 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -42,7 +42,7 @@
"Controllers": [
{
"Controller": "freezer",
- "Path": "freezer",
+ "Path": ".",
"Mode": "0755",
"UID": "system",
"GID": "system"
diff --git a/libprocessgroup/profiles/cgroups_28.json b/libprocessgroup/profiles/cgroups_28.json
index 4518487..17d4929 100644
--- a/libprocessgroup/profiles/cgroups_28.json
+++ b/libprocessgroup/profiles/cgroups_28.json
@@ -1,59 +1,11 @@
{
"Cgroups": [
{
- "Controller": "blkio",
- "Path": "/dev/blkio",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- },
- {
- "Controller": "cpu",
- "Path": "/dev/cpuctl",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- },
- {
- "Controller": "cpuacct",
- "Path": "/acct",
- "Mode": "0555"
- },
- {
- "Controller": "cpuset",
- "Path": "/dev/cpuset",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- },
- {
- "Controller": "memory",
- "Path": "/dev/memcg",
- "Mode": "0700",
- "UID": "root",
- "GID": "system"
- },
- {
"Controller": "schedtune",
"Path": "/dev/stune",
"Mode": "0755",
"UID": "system",
"GID": "system"
}
- ],
- "Cgroups2": {
- "Path": "/sys/fs/cgroup",
- "Mode": "0755",
- "UID": "system",
- "GID": "system",
- "Controllers": [
- {
- "Controller": "freezer",
- "Path": "freezer",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- }
- ]
- }
+ ]
}
diff --git a/libprocessgroup/profiles/cgroups_29.json b/libprocessgroup/profiles/cgroups_29.json
index 4518487..17d4929 100644
--- a/libprocessgroup/profiles/cgroups_29.json
+++ b/libprocessgroup/profiles/cgroups_29.json
@@ -1,59 +1,11 @@
{
"Cgroups": [
{
- "Controller": "blkio",
- "Path": "/dev/blkio",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- },
- {
- "Controller": "cpu",
- "Path": "/dev/cpuctl",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- },
- {
- "Controller": "cpuacct",
- "Path": "/acct",
- "Mode": "0555"
- },
- {
- "Controller": "cpuset",
- "Path": "/dev/cpuset",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- },
- {
- "Controller": "memory",
- "Path": "/dev/memcg",
- "Mode": "0700",
- "UID": "root",
- "GID": "system"
- },
- {
"Controller": "schedtune",
"Path": "/dev/stune",
"Mode": "0755",
"UID": "system",
"GID": "system"
}
- ],
- "Cgroups2": {
- "Path": "/sys/fs/cgroup",
- "Mode": "0755",
- "UID": "system",
- "GID": "system",
- "Controllers": [
- {
- "Controller": "freezer",
- "Path": "freezer",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- }
- ]
- }
+ ]
}
diff --git a/libprocessgroup/profiles/cgroups_30.json b/libprocessgroup/profiles/cgroups_30.json
index 4518487..17d4929 100644
--- a/libprocessgroup/profiles/cgroups_30.json
+++ b/libprocessgroup/profiles/cgroups_30.json
@@ -1,59 +1,11 @@
{
"Cgroups": [
{
- "Controller": "blkio",
- "Path": "/dev/blkio",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- },
- {
- "Controller": "cpu",
- "Path": "/dev/cpuctl",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- },
- {
- "Controller": "cpuacct",
- "Path": "/acct",
- "Mode": "0555"
- },
- {
- "Controller": "cpuset",
- "Path": "/dev/cpuset",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- },
- {
- "Controller": "memory",
- "Path": "/dev/memcg",
- "Mode": "0700",
- "UID": "root",
- "GID": "system"
- },
- {
"Controller": "schedtune",
"Path": "/dev/stune",
"Mode": "0755",
"UID": "system",
"GID": "system"
}
- ],
- "Cgroups2": {
- "Path": "/sys/fs/cgroup",
- "Mode": "0755",
- "UID": "system",
- "GID": "system",
- "Controllers": [
- {
- "Controller": "freezer",
- "Path": "freezer",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- }
- ]
- }
+ ]
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index b528fa5..628098b 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -46,7 +46,7 @@
"File": "cpu.uclamp.latency_sensitive"
},
{
- "Name": "FreezerState",
+ "Name": "Freezer",
"Controller": "freezer",
"File": "cgroup.freeze"
}
@@ -70,11 +70,11 @@
"Name": "Frozen",
"Actions": [
{
- "Name": "JoinCgroup",
+ "Name": "SetAttribute",
"Params":
{
- "Controller": "freezer",
- "Path": ""
+ "Name": "Freezer",
+ "Value": "1"
}
}
]
@@ -83,11 +83,11 @@
"Name": "Unfrozen",
"Actions": [
{
- "Name": "JoinCgroup",
+ "Name": "SetAttribute",
"Params":
{
- "Controller": "freezer",
- "Path": "../"
+ "Name": "Freezer",
+ "Value": "0"
}
}
]
@@ -106,6 +106,19 @@
]
},
{
+ "Name": "ServicePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpu",
+ "Path": "system-background"
+ }
+ }
+ ]
+ },
+ {
"Name": "HighPerformance",
"Actions": [
{
@@ -591,6 +604,10 @@
"Profiles": [ "MaxPerformance", "MaxIoPriority", "TimerSlackNormal" ]
},
{
+ "Name": "SCHED_SP_SYSTEM",
+ "Profiles": [ "ServicePerformance", "LowIoPriority", "TimerSlackNormal" ]
+ },
+ {
"Name": "SCHED_SP_RT_APP",
"Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
},
diff --git a/libprocessgroup/profiles/task_profiles_28.json b/libprocessgroup/profiles/task_profiles_28.json
index 142b0ba..9f83785 100644
--- a/libprocessgroup/profiles/task_profiles_28.json
+++ b/libprocessgroup/profiles/task_profiles_28.json
@@ -1,36 +1,6 @@
{
"Attributes": [
{
- "Name": "LowCapacityCPUs",
- "Controller": "cpuset",
- "File": "background/cpus"
- },
- {
- "Name": "HighCapacityCPUs",
- "Controller": "cpuset",
- "File": "foreground/cpus"
- },
- {
- "Name": "MaxCapacityCPUs",
- "Controller": "cpuset",
- "File": "top-app/cpus"
- },
- {
- "Name": "MemLimit",
- "Controller": "memory",
- "File": "memory.limit_in_bytes"
- },
- {
- "Name": "MemSoftLimit",
- "Controller": "memory",
- "File": "memory.soft_limit_in_bytes"
- },
- {
- "Name": "MemSwappiness",
- "Controller": "memory",
- "File": "memory.swappiness"
- },
- {
"Name": "STuneBoost",
"Controller": "schedtune",
"File": "schedtune.boost"
@@ -39,21 +9,6 @@
"Name": "STunePreferIdle",
"Controller": "schedtune",
"File": "schedtune.prefer_idle"
- },
- {
- "Name": "UClampMin",
- "Controller": "cpu",
- "File": "cpu.uclamp.min"
- },
- {
- "Name": "UClampMax",
- "Controller": "cpu",
- "File": "cpu.uclamp.max"
- },
- {
- "Name": "FreezerState",
- "Controller": "freezer",
- "File": "cgroup.freeze"
}
],
@@ -72,32 +27,6 @@
]
},
{
- "Name": "Frozen",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "freezer",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "Unfrozen",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "freezer",
- "Path": "../"
- }
- }
- ]
- },
- {
"Name": "NormalPerformance",
"Actions": [
{
@@ -201,427 +130,6 @@
}
}
]
- },
-
- {
- "Name": "VrKernelCapacity",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "VrServiceCapacityLow",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system/background"
- }
- }
- ]
- },
- {
- "Name": "VrServiceCapacityNormal",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system"
- }
- }
- ]
- },
- {
- "Name": "VrServiceCapacityHigh",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system/performance"
- }
- }
- ]
- },
- {
- "Name": "VrProcessCapacityLow",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "application/background"
- }
- }
- ]
- },
- {
- "Name": "VrProcessCapacityNormal",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "application"
- }
- }
- ]
- },
- {
- "Name": "VrProcessCapacityHigh",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "application/performance"
- }
- }
- ]
- },
-
- {
- "Name": "ProcessCapacityLow",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "background"
- }
- }
- ]
- },
- {
- "Name": "ProcessCapacityNormal",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "ProcessCapacityHigh",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "foreground"
- }
- }
- ]
- },
- {
- "Name": "ProcessCapacityMax",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "top-app"
- }
- }
- ]
- },
-
- {
- "Name": "ServiceCapacityLow",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system-background"
- }
- }
- ]
- },
- {
- "Name": "ServiceCapacityRestricted",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "restricted"
- }
- }
- ]
- },
-
- {
- "Name": "CameraServiceCapacity",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "camera-daemon"
- }
- }
- ]
- },
-
- {
- "Name": "LowIoPriority",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "blkio",
- "Path": "background"
- }
- }
- ]
- },
- {
- "Name": "NormalIoPriority",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "blkio",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "HighIoPriority",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "blkio",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "MaxIoPriority",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "blkio",
- "Path": ""
- }
- }
- ]
- },
-
- {
- "Name": "TimerSlackHigh",
- "Actions": [
- {
- "Name": "SetTimerSlack",
- "Params":
- {
- "Slack": "40000000"
- }
- }
- ]
- },
- {
- "Name": "TimerSlackNormal",
- "Actions": [
- {
- "Name": "SetTimerSlack",
- "Params":
- {
- "Slack": "50000"
- }
- }
- ]
- },
-
- {
- "Name": "PerfBoost",
- "Actions": [
- {
- "Name": "SetClamps",
- "Params":
- {
- "Boost": "50%",
- "Clamp": "0"
- }
- }
- ]
- },
- {
- "Name": "PerfClamp",
- "Actions": [
- {
- "Name": "SetClamps",
- "Params":
- {
- "Boost": "0",
- "Clamp": "30%"
- }
- }
- ]
- },
-
- {
- "Name": "LowMemoryUsage",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "MemSoftLimit",
- "Value": "16MB"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "MemSwappiness",
- "Value": "150"
-
- }
- }
- ]
- },
- {
- "Name": "HighMemoryUsage",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "MemSoftLimit",
- "Value": "512MB"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "MemSwappiness",
- "Value": "100"
- }
- }
- ]
- },
- {
- "Name": "SystemMemoryProcess",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "memory",
- "Path": "system"
- }
- }
- ]
- },
- {
- "Name": "FreezerDisabled",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "FreezerState",
- "Value": "0"
- }
- }
- ]
- },
- {
- "Name": "FreezerEnabled",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "FreezerState",
- "Value": "1"
- }
- }
- ]
- }
- ],
-
- "AggregateProfiles": [
- {
- "Name": "SCHED_SP_DEFAULT",
- "Profiles": [ "TimerSlackNormal" ]
- },
- {
- "Name": "SCHED_SP_BACKGROUND",
- "Profiles": [ "HighEnergySaving", "LowIoPriority", "TimerSlackHigh" ]
- },
- {
- "Name": "SCHED_SP_FOREGROUND",
- "Profiles": [ "HighPerformance", "HighIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "SCHED_SP_TOP_APP",
- "Profiles": [ "MaxPerformance", "MaxIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "SCHED_SP_RT_APP",
- "Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_DEFAULT",
- "Profiles": [ "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_BACKGROUND",
- "Profiles": [ "HighEnergySaving", "ProcessCapacityLow", "LowIoPriority", "TimerSlackHigh" ]
- },
- {
- "Name": "CPUSET_SP_FOREGROUND",
- "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "HighIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_TOP_APP",
- "Profiles": [ "MaxPerformance", "ProcessCapacityMax", "MaxIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_SYSTEM",
- "Profiles": [ "ServiceCapacityLow", "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_RESTRICTED",
- "Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
}
]
}
diff --git a/libprocessgroup/profiles/task_profiles_29.json b/libprocessgroup/profiles/task_profiles_29.json
index 142b0ba..9f83785 100644
--- a/libprocessgroup/profiles/task_profiles_29.json
+++ b/libprocessgroup/profiles/task_profiles_29.json
@@ -1,36 +1,6 @@
{
"Attributes": [
{
- "Name": "LowCapacityCPUs",
- "Controller": "cpuset",
- "File": "background/cpus"
- },
- {
- "Name": "HighCapacityCPUs",
- "Controller": "cpuset",
- "File": "foreground/cpus"
- },
- {
- "Name": "MaxCapacityCPUs",
- "Controller": "cpuset",
- "File": "top-app/cpus"
- },
- {
- "Name": "MemLimit",
- "Controller": "memory",
- "File": "memory.limit_in_bytes"
- },
- {
- "Name": "MemSoftLimit",
- "Controller": "memory",
- "File": "memory.soft_limit_in_bytes"
- },
- {
- "Name": "MemSwappiness",
- "Controller": "memory",
- "File": "memory.swappiness"
- },
- {
"Name": "STuneBoost",
"Controller": "schedtune",
"File": "schedtune.boost"
@@ -39,21 +9,6 @@
"Name": "STunePreferIdle",
"Controller": "schedtune",
"File": "schedtune.prefer_idle"
- },
- {
- "Name": "UClampMin",
- "Controller": "cpu",
- "File": "cpu.uclamp.min"
- },
- {
- "Name": "UClampMax",
- "Controller": "cpu",
- "File": "cpu.uclamp.max"
- },
- {
- "Name": "FreezerState",
- "Controller": "freezer",
- "File": "cgroup.freeze"
}
],
@@ -72,32 +27,6 @@
]
},
{
- "Name": "Frozen",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "freezer",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "Unfrozen",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "freezer",
- "Path": "../"
- }
- }
- ]
- },
- {
"Name": "NormalPerformance",
"Actions": [
{
@@ -201,427 +130,6 @@
}
}
]
- },
-
- {
- "Name": "VrKernelCapacity",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "VrServiceCapacityLow",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system/background"
- }
- }
- ]
- },
- {
- "Name": "VrServiceCapacityNormal",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system"
- }
- }
- ]
- },
- {
- "Name": "VrServiceCapacityHigh",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system/performance"
- }
- }
- ]
- },
- {
- "Name": "VrProcessCapacityLow",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "application/background"
- }
- }
- ]
- },
- {
- "Name": "VrProcessCapacityNormal",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "application"
- }
- }
- ]
- },
- {
- "Name": "VrProcessCapacityHigh",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "application/performance"
- }
- }
- ]
- },
-
- {
- "Name": "ProcessCapacityLow",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "background"
- }
- }
- ]
- },
- {
- "Name": "ProcessCapacityNormal",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "ProcessCapacityHigh",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "foreground"
- }
- }
- ]
- },
- {
- "Name": "ProcessCapacityMax",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "top-app"
- }
- }
- ]
- },
-
- {
- "Name": "ServiceCapacityLow",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system-background"
- }
- }
- ]
- },
- {
- "Name": "ServiceCapacityRestricted",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "restricted"
- }
- }
- ]
- },
-
- {
- "Name": "CameraServiceCapacity",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "camera-daemon"
- }
- }
- ]
- },
-
- {
- "Name": "LowIoPriority",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "blkio",
- "Path": "background"
- }
- }
- ]
- },
- {
- "Name": "NormalIoPriority",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "blkio",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "HighIoPriority",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "blkio",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "MaxIoPriority",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "blkio",
- "Path": ""
- }
- }
- ]
- },
-
- {
- "Name": "TimerSlackHigh",
- "Actions": [
- {
- "Name": "SetTimerSlack",
- "Params":
- {
- "Slack": "40000000"
- }
- }
- ]
- },
- {
- "Name": "TimerSlackNormal",
- "Actions": [
- {
- "Name": "SetTimerSlack",
- "Params":
- {
- "Slack": "50000"
- }
- }
- ]
- },
-
- {
- "Name": "PerfBoost",
- "Actions": [
- {
- "Name": "SetClamps",
- "Params":
- {
- "Boost": "50%",
- "Clamp": "0"
- }
- }
- ]
- },
- {
- "Name": "PerfClamp",
- "Actions": [
- {
- "Name": "SetClamps",
- "Params":
- {
- "Boost": "0",
- "Clamp": "30%"
- }
- }
- ]
- },
-
- {
- "Name": "LowMemoryUsage",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "MemSoftLimit",
- "Value": "16MB"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "MemSwappiness",
- "Value": "150"
-
- }
- }
- ]
- },
- {
- "Name": "HighMemoryUsage",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "MemSoftLimit",
- "Value": "512MB"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "MemSwappiness",
- "Value": "100"
- }
- }
- ]
- },
- {
- "Name": "SystemMemoryProcess",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "memory",
- "Path": "system"
- }
- }
- ]
- },
- {
- "Name": "FreezerDisabled",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "FreezerState",
- "Value": "0"
- }
- }
- ]
- },
- {
- "Name": "FreezerEnabled",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "FreezerState",
- "Value": "1"
- }
- }
- ]
- }
- ],
-
- "AggregateProfiles": [
- {
- "Name": "SCHED_SP_DEFAULT",
- "Profiles": [ "TimerSlackNormal" ]
- },
- {
- "Name": "SCHED_SP_BACKGROUND",
- "Profiles": [ "HighEnergySaving", "LowIoPriority", "TimerSlackHigh" ]
- },
- {
- "Name": "SCHED_SP_FOREGROUND",
- "Profiles": [ "HighPerformance", "HighIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "SCHED_SP_TOP_APP",
- "Profiles": [ "MaxPerformance", "MaxIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "SCHED_SP_RT_APP",
- "Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_DEFAULT",
- "Profiles": [ "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_BACKGROUND",
- "Profiles": [ "HighEnergySaving", "ProcessCapacityLow", "LowIoPriority", "TimerSlackHigh" ]
- },
- {
- "Name": "CPUSET_SP_FOREGROUND",
- "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "HighIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_TOP_APP",
- "Profiles": [ "MaxPerformance", "ProcessCapacityMax", "MaxIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_SYSTEM",
- "Profiles": [ "ServiceCapacityLow", "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_RESTRICTED",
- "Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
}
]
}
diff --git a/libprocessgroup/profiles/task_profiles_30.json b/libprocessgroup/profiles/task_profiles_30.json
index 142b0ba..9f83785 100644
--- a/libprocessgroup/profiles/task_profiles_30.json
+++ b/libprocessgroup/profiles/task_profiles_30.json
@@ -1,36 +1,6 @@
{
"Attributes": [
{
- "Name": "LowCapacityCPUs",
- "Controller": "cpuset",
- "File": "background/cpus"
- },
- {
- "Name": "HighCapacityCPUs",
- "Controller": "cpuset",
- "File": "foreground/cpus"
- },
- {
- "Name": "MaxCapacityCPUs",
- "Controller": "cpuset",
- "File": "top-app/cpus"
- },
- {
- "Name": "MemLimit",
- "Controller": "memory",
- "File": "memory.limit_in_bytes"
- },
- {
- "Name": "MemSoftLimit",
- "Controller": "memory",
- "File": "memory.soft_limit_in_bytes"
- },
- {
- "Name": "MemSwappiness",
- "Controller": "memory",
- "File": "memory.swappiness"
- },
- {
"Name": "STuneBoost",
"Controller": "schedtune",
"File": "schedtune.boost"
@@ -39,21 +9,6 @@
"Name": "STunePreferIdle",
"Controller": "schedtune",
"File": "schedtune.prefer_idle"
- },
- {
- "Name": "UClampMin",
- "Controller": "cpu",
- "File": "cpu.uclamp.min"
- },
- {
- "Name": "UClampMax",
- "Controller": "cpu",
- "File": "cpu.uclamp.max"
- },
- {
- "Name": "FreezerState",
- "Controller": "freezer",
- "File": "cgroup.freeze"
}
],
@@ -72,32 +27,6 @@
]
},
{
- "Name": "Frozen",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "freezer",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "Unfrozen",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "freezer",
- "Path": "../"
- }
- }
- ]
- },
- {
"Name": "NormalPerformance",
"Actions": [
{
@@ -201,427 +130,6 @@
}
}
]
- },
-
- {
- "Name": "VrKernelCapacity",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "VrServiceCapacityLow",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system/background"
- }
- }
- ]
- },
- {
- "Name": "VrServiceCapacityNormal",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system"
- }
- }
- ]
- },
- {
- "Name": "VrServiceCapacityHigh",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system/performance"
- }
- }
- ]
- },
- {
- "Name": "VrProcessCapacityLow",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "application/background"
- }
- }
- ]
- },
- {
- "Name": "VrProcessCapacityNormal",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "application"
- }
- }
- ]
- },
- {
- "Name": "VrProcessCapacityHigh",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "application/performance"
- }
- }
- ]
- },
-
- {
- "Name": "ProcessCapacityLow",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "background"
- }
- }
- ]
- },
- {
- "Name": "ProcessCapacityNormal",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "ProcessCapacityHigh",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "foreground"
- }
- }
- ]
- },
- {
- "Name": "ProcessCapacityMax",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "top-app"
- }
- }
- ]
- },
-
- {
- "Name": "ServiceCapacityLow",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system-background"
- }
- }
- ]
- },
- {
- "Name": "ServiceCapacityRestricted",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "restricted"
- }
- }
- ]
- },
-
- {
- "Name": "CameraServiceCapacity",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "camera-daemon"
- }
- }
- ]
- },
-
- {
- "Name": "LowIoPriority",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "blkio",
- "Path": "background"
- }
- }
- ]
- },
- {
- "Name": "NormalIoPriority",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "blkio",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "HighIoPriority",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "blkio",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "MaxIoPriority",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "blkio",
- "Path": ""
- }
- }
- ]
- },
-
- {
- "Name": "TimerSlackHigh",
- "Actions": [
- {
- "Name": "SetTimerSlack",
- "Params":
- {
- "Slack": "40000000"
- }
- }
- ]
- },
- {
- "Name": "TimerSlackNormal",
- "Actions": [
- {
- "Name": "SetTimerSlack",
- "Params":
- {
- "Slack": "50000"
- }
- }
- ]
- },
-
- {
- "Name": "PerfBoost",
- "Actions": [
- {
- "Name": "SetClamps",
- "Params":
- {
- "Boost": "50%",
- "Clamp": "0"
- }
- }
- ]
- },
- {
- "Name": "PerfClamp",
- "Actions": [
- {
- "Name": "SetClamps",
- "Params":
- {
- "Boost": "0",
- "Clamp": "30%"
- }
- }
- ]
- },
-
- {
- "Name": "LowMemoryUsage",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "MemSoftLimit",
- "Value": "16MB"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "MemSwappiness",
- "Value": "150"
-
- }
- }
- ]
- },
- {
- "Name": "HighMemoryUsage",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "MemSoftLimit",
- "Value": "512MB"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "MemSwappiness",
- "Value": "100"
- }
- }
- ]
- },
- {
- "Name": "SystemMemoryProcess",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "memory",
- "Path": "system"
- }
- }
- ]
- },
- {
- "Name": "FreezerDisabled",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "FreezerState",
- "Value": "0"
- }
- }
- ]
- },
- {
- "Name": "FreezerEnabled",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "FreezerState",
- "Value": "1"
- }
- }
- ]
- }
- ],
-
- "AggregateProfiles": [
- {
- "Name": "SCHED_SP_DEFAULT",
- "Profiles": [ "TimerSlackNormal" ]
- },
- {
- "Name": "SCHED_SP_BACKGROUND",
- "Profiles": [ "HighEnergySaving", "LowIoPriority", "TimerSlackHigh" ]
- },
- {
- "Name": "SCHED_SP_FOREGROUND",
- "Profiles": [ "HighPerformance", "HighIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "SCHED_SP_TOP_APP",
- "Profiles": [ "MaxPerformance", "MaxIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "SCHED_SP_RT_APP",
- "Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_DEFAULT",
- "Profiles": [ "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_BACKGROUND",
- "Profiles": [ "HighEnergySaving", "ProcessCapacityLow", "LowIoPriority", "TimerSlackHigh" ]
- },
- {
- "Name": "CPUSET_SP_FOREGROUND",
- "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "HighIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_TOP_APP",
- "Profiles": [ "MaxPerformance", "ProcessCapacityMax", "MaxIoPriority", "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_SYSTEM",
- "Profiles": [ "ServiceCapacityLow", "TimerSlackNormal" ]
- },
- {
- "Name": "CPUSET_SP_RESTRICTED",
- "Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
}
]
}
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 698e74d..c51ee61 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -124,6 +124,8 @@
return SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
case SP_TOP_APP:
return SetTaskProfiles(tid, {"SCHED_SP_TOP_APP"}, true) ? 0 : -1;
+ case SP_SYSTEM:
+ return SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1;
case SP_RT_APP:
return SetTaskProfiles(tid, {"SCHED_SP_RT_APP"}, true) ? 0 : -1;
default:
@@ -258,7 +260,7 @@
*/
static constexpr const char* kSchedProfiles[SP_CNT + 1] = {
"SCHED_SP_DEFAULT", "SCHED_SP_BACKGROUND", "SCHED_SP_FOREGROUND",
- "SCHED_SP_DEFAULT", "SCHED_SP_FOREGROUND", "SCHED_SP_FOREGROUND",
+ "SCHED_SP_SYSTEM", "SCHED_SP_FOREGROUND", "SCHED_SP_FOREGROUND",
"SCHED_SP_TOP_APP", "SCHED_SP_RT_APP", "SCHED_SP_DEFAULT"};
if (policy < SP_DEFAULT || policy >= SP_CNT) {
return nullptr;
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index a53132e..753fd2d 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -214,23 +214,23 @@
}
static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
- unsigned int api_level = GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
- std::string sys_cgroups_path = CGROUPS_DESC_FILE;
+ // load system cgroup descriptors
+ if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {
+ return false;
+ }
// load API-level specific system cgroups descriptors if available
+ unsigned int api_level = GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
if (api_level > 0) {
std::string api_cgroups_path =
android::base::StringPrintf(TEMPLATE_CGROUPS_DESC_API_FILE, api_level);
if (!access(api_cgroups_path.c_str(), F_OK) || errno != ENOENT) {
- sys_cgroups_path = api_cgroups_path;
+ if (!ReadDescriptorsFromFile(api_cgroups_path, descriptors)) {
+ return false;
+ }
}
}
- // load system cgroup descriptors
- if (!ReadDescriptorsFromFile(sys_cgroups_path, descriptors)) {
- return false;
- }
-
// load vendor cgroup descriptors if the file exists
if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
!ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index db44228..1311306 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -391,23 +391,24 @@
}
TaskProfiles::TaskProfiles() {
- unsigned int api_level = GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
- std::string sys_profiles_path = TASK_PROFILE_DB_FILE;
+ // load system task profiles
+ if (!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_FILE)) {
+ LOG(ERROR) << "Loading " << TASK_PROFILE_DB_FILE << " for [" << getpid() << "] failed";
+ }
// load API-level specific system task profiles if available
+ unsigned int api_level = GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
if (api_level > 0) {
std::string api_profiles_path =
android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level);
if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) {
- sys_profiles_path = api_profiles_path;
+ if (!Load(CgroupMap::GetInstance(), api_profiles_path)) {
+ LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid()
+ << "] failed";
+ }
}
}
- // load system task profiles
- if (!Load(CgroupMap::GetInstance(), sys_profiles_path)) {
- LOG(ERROR) << "Loading " << sys_profiles_path << " for [" << getpid() << "] failed";
- }
-
// load vendor task profiles if the file exists
if (!access(TASK_PROFILE_DB_VENDOR_FILE, F_OK) &&
!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_VENDOR_FILE)) {
diff --git a/libstats/pull/Android.bp b/libstats/pull/Android.bp
index a8b4a4f..6d6b466 100644
--- a/libstats/pull/Android.bp
+++ b/libstats/pull/Android.bp
@@ -70,6 +70,7 @@
],
visibility: [
"//frameworks/base/apex/statsd/tests/libstatspull",
+ "//packages/modules/StatsD/apex/tests/libstatspull",
],
}
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index 89cdfe5..a6ea185 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -79,6 +79,8 @@
visibility: [
"//frameworks/base/apex/statsd/tests/libstatspull",
"//frameworks/base/cmds/statsd",
+ "//packages/modules/StatsD/apex/tests/libstatspull",
+ "//packages/modules/StatsD/bin",
],
}
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index 12c946c..b37b8ec 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -1,6 +1,7 @@
cc_library_headers {
name: "libsystem_headers",
vendor_available: true,
+ product_available: true,
recovery_available: true,
vendor_ramdisk_available: true,
host_supported: true,
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 8ee16f3..1e7cbdb 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -15,6 +15,7 @@
cc_library_headers {
name: "libutils_headers",
vendor_available: true,
+ product_available: true,
recovery_available: true,
vendor_ramdisk_available: true,
host_supported: true,
@@ -62,6 +63,7 @@
cc_defaults {
name: "libutils_defaults",
vendor_available: true,
+ product_available: true,
recovery_available: true,
vndk: {
enabled: true,
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 2bceb75..17d71f7 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -78,7 +78,7 @@
# create some directories (some are mount points) and symlinks
LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
dev proc sys system data data_mirror odm oem acct config storage mnt apex debug_ramdisk \
- linkerconfig second_stage_resources $(BOARD_ROOT_EXTRA_FOLDERS)); \
+ linkerconfig second_stage_resources postinstall $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
@@ -153,11 +153,6 @@
; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
endif
-# The A/B updater uses a top-level /postinstall directory to mount the new
-# system before reboot.
-ifeq ($(AB_OTA_UPDATER),true)
- LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/postinstall
-endif
# The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
# Since init.environ.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 42a12b7..de608b1 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -56,7 +56,7 @@
write /sys/module/dm_verity/parameters/prefetch_cluster 0
# Generate ld.config.txt for early executed processes
- exec -- /system/bin/linkerconfig --target /linkerconfig/bootstrap
+ exec -- /system/bin/bootstrap/linkerconfig --target /linkerconfig/bootstrap
chmod 644 /linkerconfig/bootstrap/ld.config.txt
copy /linkerconfig/bootstrap/ld.config.txt /linkerconfig/default/ld.config.txt
chmod 644 /linkerconfig/default/ld.config.txt
@@ -154,24 +154,28 @@
mkdir /dev/cpuctl/top-app
mkdir /dev/cpuctl/rt
mkdir /dev/cpuctl/system
+ mkdir /dev/cpuctl/system-background
chown system system /dev/cpuctl
chown system system /dev/cpuctl/foreground
chown system system /dev/cpuctl/background
chown system system /dev/cpuctl/top-app
chown system system /dev/cpuctl/rt
chown system system /dev/cpuctl/system
+ chown system system /dev/cpuctl/system-background
chown system system /dev/cpuctl/tasks
chown system system /dev/cpuctl/foreground/tasks
chown system system /dev/cpuctl/background/tasks
chown system system /dev/cpuctl/top-app/tasks
chown system system /dev/cpuctl/rt/tasks
chown system system /dev/cpuctl/system/tasks
+ chown system system /dev/cpuctl/system-background/tasks
chmod 0664 /dev/cpuctl/tasks
chmod 0664 /dev/cpuctl/foreground/tasks
chmod 0664 /dev/cpuctl/background/tasks
chmod 0664 /dev/cpuctl/top-app/tasks
chmod 0664 /dev/cpuctl/rt/tasks
chmod 0664 /dev/cpuctl/system/tasks
+ chmod 0664 /dev/cpuctl/system-background/tasks
# Create a cpu group for NNAPI HAL processes
mkdir /dev/cpuctl/nnapi-hal
@@ -196,7 +200,7 @@
# Surfaceflinger is in FG group so giving it a bit more
write /dev/cpuctl/foreground/cpu.rt_runtime_us 450000
write /dev/cpuctl/foreground/cpu.rt_period_us 1000000
- write /dev/cpuctl/background/cpu.rt_runtime_us 100000
+ write /dev/cpuctl/background/cpu.rt_runtime_us 50000
write /dev/cpuctl/background/cpu.rt_period_us 1000000
write /dev/cpuctl/top-app/cpu.rt_runtime_us 100000
write /dev/cpuctl/top-app/cpu.rt_period_us 1000000
@@ -204,12 +208,22 @@
write /dev/cpuctl/rt/cpu.rt_period_us 1000000
write /dev/cpuctl/system/cpu.rt_runtime_us 100000
write /dev/cpuctl/system/cpu.rt_period_us 1000000
- write /dev/cpuctl/nnapi-hal/cpu.rt_runtime_us 100000
+ write /dev/cpuctl/system-background/cpu.rt_runtime_us 50000
+ write /dev/cpuctl/system-background/cpu.rt_period_us 1000000
+ write /dev/cpuctl/nnapi-hal/cpu.rt_runtime_us 50000
write /dev/cpuctl/nnapi-hal/cpu.rt_period_us 1000000
+ write /dev/cpuctl/camera-daemon/cpu.rt_runtime_us 50000
+ write /dev/cpuctl/camera-daemon/cpu.rt_period_us 1000000
# Migrate root group to system subgroup
copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
+ # Create an stune group for camera-specific processes
+ mkdir /dev/stune/camera-daemon
+ chown system system /dev/stune/camera-daemon
+ chown system system /dev/stune/camera-daemon/tasks
+ chmod 0664 /dev/stune/camera-daemon/tasks
+
# Create an stune group for NNAPI HAL processes
mkdir /dev/stune/nnapi-hal
chown system system /dev/stune/nnapi-hal
@@ -341,7 +355,6 @@
# system-background is for system tasks that should only run on
# little cores, not on bigs
- # to be used only by init, so don't change system-bg permissions
mkdir /dev/cpuset/system-background
copy /dev/cpuset/cpus /dev/cpuset/system-background/cpus
copy /dev/cpuset/mems /dev/cpuset/system-background/mems
@@ -599,6 +612,9 @@
# HALs required before storage encryption can get unlocked (FBE/FDE)
class_start early_hal
+ # Load trusted keys from dm-verity protected partitions
+ exec -- /system/bin/fsverity_init --load-verified-keys
+
on post-fs-data
mark_post_data
@@ -840,6 +856,9 @@
wait_for_prop apexd.status activated
perform_apex_config
+ # Lock the fs-verity keyring, so no more keys can be added
+ exec -- /system/bin/fsverity_init --lock
+
# After apexes are mounted, tell keymaster early boot has ended, so it will
# stop allowing use of early-boot keys
exec - system system -- /system/bin/vdc keymaster earlyBootEnded
@@ -1021,9 +1040,6 @@
class_start core
- # Requires keystore (currently a core service) to be ready first.
- exec -- /system/bin/fsverity_init
-
on nonencrypted
class_start main
class_start late_start