summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-12-23 08:29:24 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-12-23 08:29:24 +0000
commit07137ed68826e5d41d23e5cf083885bc919ce11d (patch)
tree893505a763a8280b4627ce8132d7039a911e6304
parent2f61e4c39863cdad85e0fdfaacc40d10c35e6479 (diff)
parent0095bebba6a5574aa0abafbc63ac0ca2a6f85080 (diff)
Snap for 8024443 from 0095bebba6a5574aa0abafbc63ac0ca2a6f85080 to s-keystone-qcom-release
Change-Id: Ic1e47b52616262f935b586524fc89a70cad048e3
-rw-r--r--fastboot/device/flashing.cpp18
-rw-r--r--fs_mgr/fs_mgr_fstab.cpp17
-rw-r--r--fs_mgr/libsnapshot/cow_snapuserd_test.cpp74
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/snapshot.h1
-rw-r--r--fs_mgr/libsnapshot/snapshot.cpp40
-rw-r--r--fs_mgr/libsnapshot/snapshot_test.cpp76
-rw-r--r--fs_mgr/libsnapshot/snapuserd_worker.cpp28
-rw-r--r--init/init.cpp15
-rw-r--r--libprocessgroup/profiles/task_profiles_28.json13
-rw-r--r--libprocessgroup/profiles/task_profiles_29.json13
-rw-r--r--libprocessgroup/profiles/task_profiles_30.json13
-rw-r--r--libutils/Threads.cpp6
-rw-r--r--rootdir/init.rc16
-rw-r--r--trusty/storage/interface/include/trusty/interface/storage.h40
-rw-r--r--trusty/storage/proxy/Android.bp3
-rw-r--r--trusty/storage/proxy/checkpoint_handling.cpp77
-rw-r--r--trusty/storage/proxy/checkpoint_handling.h37
-rw-r--r--trusty/storage/proxy/proxy.c16
-rw-r--r--trusty/storage/proxy/rpmb.c263
19 files changed, 692 insertions, 74 deletions
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index ee0aa582b..9b5d2cd25 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -187,11 +187,17 @@ bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wip
", build may be missing broken or missing boot_devices");
}
+ std::string slot_suffix = device->GetCurrentSlot();
+ uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+
+ std::string other_slot_suffix;
+ if (!slot_suffix.empty()) {
+ other_slot_suffix = (slot_suffix == "_a") ? "_b" : "_a";
+ }
+
// If we are unable to read the existing metadata, then the super partition
// is corrupt. In this case we reflash the whole thing using the provided
// image.
- std::string slot_suffix = device->GetCurrentSlot();
- uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
std::unique_ptr<LpMetadata> old_metadata = ReadMetadata(super_name, slot_number);
if (wipe || !old_metadata) {
if (!FlashPartitionTable(super_name, *new_metadata.get())) {
@@ -203,11 +209,15 @@ bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wip
}
std::set<std::string> partitions_to_keep;
+ bool virtual_ab = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
for (const auto& partition : old_metadata->partitions) {
// Preserve partitions in the other slot, but not the current slot.
std::string partition_name = GetPartitionName(partition);
- if (!slot_suffix.empty() && GetPartitionSlotSuffix(partition_name) == slot_suffix) {
- continue;
+ if (!slot_suffix.empty()) {
+ auto part_suffix = GetPartitionSlotSuffix(partition_name);
+ if (part_suffix == slot_suffix || (part_suffix == other_slot_suffix && virtual_ab)) {
+ continue;
+ }
}
std::string group_name = GetPartitionGroupName(old_metadata->groups[partition.group_index]);
// Skip partitions in the COW group
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 9a2332445..7fc73bdc5 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -414,17 +414,24 @@ std::string ReadFstabFromDt() {
return fstab_result;
}
-// Identify path to fstab file. Lookup is based on pattern
-// fstab.<fstab_suffix>, fstab.<hardware>, fstab.<hardware.platform> in
-// folders /odm/etc, vendor/etc, or /.
+// Return the path to the fstab file. There may be multiple fstab files; the
+// one that is returned will be the first that exists of fstab.<fstab_suffix>,
+// fstab.<hardware>, and fstab.<hardware.platform>. The fstab is searched for
+// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
+// the first stage ramdisk during early boot. Previously, the first stage
+// ramdisk's copy of the fstab had to be located in the root directory, but now
+// the system/etc directory is supported too and is the preferred location.
std::string GetFstabPath() {
for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
std::string suffix;
if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
- for (const char* prefix :
- {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab.", "/first_stage_ramdisk/fstab."}) {
+ for (const char* prefix : {// late-boot/post-boot locations
+ "/odm/etc/fstab.", "/vendor/etc/fstab.",
+ // early boot locations
+ "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
+ "/fstab.", "/first_stage_ramdisk/fstab."}) {
std::string fstab_path = prefix + suffix;
if (access(fstab_path.c_str(), F_OK) == 0) {
return fstab_path;
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index d09c6e9c9..bd432bbdf 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -108,6 +108,7 @@ class CowSnapuserdTest final {
void MergeInterruptFixed(int duration);
void MergeInterruptRandomly(int max_duration);
void ReadDmUserBlockWithoutDaemon();
+ void ReadLastBlock();
std::string snapshot_dev() const { return snapshot_dev_->path(); }
@@ -256,6 +257,73 @@ void CowSnapuserdTest::StartSnapuserdDaemon() {
}
}
+void CowSnapuserdTest::ReadLastBlock() {
+ unique_fd rnd_fd;
+ total_base_size_ = BLOCK_SZ * 2;
+
+ 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);
+
+ std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
+
+ for (size_t j = 0; j < ((total_base_size_) / BLOCK_SZ); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), BLOCK_SZ, 0), true);
+ ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), BLOCK_SZ), true);
+ }
+
+ ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
+
+ base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
+ ASSERT_TRUE(base_loop_->valid());
+
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
+
+ std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(total_base_size_);
+ loff_t offset = 0;
+
+ // Fill random data
+ for (size_t j = 0; j < (total_base_size_ / BLOCK_SZ); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, BLOCK_SZ, 0),
+ true);
+
+ offset += BLOCK_SZ;
+ }
+
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+
+ ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), BLOCK_SZ));
+ ASSERT_TRUE(writer.AddRawBlocks(1, (char*)random_buffer_1_.get() + BLOCK_SZ, BLOCK_SZ));
+
+ ASSERT_TRUE(writer.Finalize());
+
+ SetDeviceControlName();
+
+ StartSnapuserdDaemon();
+ InitCowDevice();
+
+ CreateDmUserDevice();
+ InitDaemon();
+
+ CreateSnapshotDevice();
+
+ 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[]>(BLOCK_SZ);
+
+ offset = 7680;
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), 512, offset), true);
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)random_buffer_1_.get() + offset, 512), 0);
+}
+
void CowSnapuserdTest::CreateBaseDevice() {
unique_fd rnd_fd;
@@ -1068,6 +1136,12 @@ TEST(Snapuserd_Test, Snapshot_IO_TEST) {
harness.Shutdown();
}
+TEST(Snapuserd_Test, Snapshot_END_IO_TEST) {
+ CowSnapuserdTest harness;
+ harness.ReadLastBlock();
+ harness.Shutdown();
+}
+
TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_1) {
CowSnapuserdTest harness;
ASSERT_TRUE(harness.SetupCopyOverlap_1());
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 15882b382..9bf5db18e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -399,6 +399,7 @@ class SnapshotManager final : public ISnapshotManager {
FRIEND_TEST(SnapshotTest, MergeFailureCode);
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+ FRIEND_TEST(SnapshotUpdateTest, AddPartition);
FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 0e36da151..4c94da28f 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -518,6 +518,13 @@ bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
break;
}
+ if (mode == SnapshotStorageMode::Persistent && status.state() == SnapshotState::MERGING) {
+ LOG(ERROR) << "Snapshot: " << name
+ << " has snapshot status Merging but mode set to Persistent."
+ << " Changing mode to Snapshot-Merge.";
+ mode = SnapshotStorageMode::Merge;
+ }
+
DmTable table;
table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,
kSnapshotChunkSize);
@@ -886,6 +893,10 @@ bool SnapshotManager::QuerySnapshotStatus(const std::string& dm_name, std::strin
if (target_type) {
*target_type = DeviceMapper::GetTargetType(target.spec);
}
+ if (!status->error.empty()) {
+ LOG(ERROR) << "Snapshot: " << dm_name << " returned error code: " << status->error;
+ return false;
+ }
return true;
}
@@ -1456,7 +1467,7 @@ bool SnapshotManager::PerformInitTransition(InitTransition transition,
std::vector<std::string>* snapuserd_argv) {
LOG(INFO) << "Performing transition for snapuserd.";
- // Don't use EnsuerSnapuserdConnected() because this is called from init,
+ // Don't use EnsureSnapuserdConnected() because this is called from init,
// and attempting to do so will deadlock.
if (!snapuserd_client_ && transition != InitTransition::SELINUX_DETACH) {
snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
@@ -1513,8 +1524,15 @@ bool SnapshotManager::PerformInitTransition(InitTransition transition,
continue;
}
+ std::string source_device_name;
+ if (snapshot_status.old_partition_size() > 0) {
+ source_device_name = GetSourceDeviceName(snapshot);
+ } else {
+ source_device_name = GetBaseDeviceName(snapshot);
+ }
+
std::string source_device;
- if (!dm.GetDmDevicePathByName(GetSourceDeviceName(snapshot), &source_device)) {
+ if (!dm.GetDmDevicePathByName(source_device_name, &source_device)) {
LOG(ERROR) << "Could not get device path for " << GetSourceDeviceName(snapshot);
continue;
}
@@ -2091,14 +2109,18 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
if (live_snapshot_status->compression_enabled()) {
// Get the source device (eg the view of the partition from before it was resized).
std::string source_device_path;
- if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time,
- &source_device_path)) {
- LOG(ERROR) << "Could not map source device for: " << cow_name;
- return false;
- }
+ if (live_snapshot_status->old_partition_size() > 0) {
+ if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time,
+ &source_device_path)) {
+ LOG(ERROR) << "Could not map source device for: " << cow_name;
+ return false;
+ }
- auto source_device = GetSourceDeviceName(params.GetPartitionName());
- created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device);
+ auto source_device = GetSourceDeviceName(params.GetPartitionName());
+ created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device);
+ } else {
+ source_device_path = base_path;
+ }
if (!WaitForDevice(source_device_path, remaining_time)) {
return false;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 60186434a..7630efe3f 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -963,7 +963,7 @@ class SnapshotUpdateTest : public SnapshotTest {
}
AssertionResult UnmapAll() {
- for (const auto& name : {"sys", "vnd", "prd"}) {
+ for (const auto& name : {"sys", "vnd", "prd", "dlkm"}) {
if (!dm_.DeleteDeviceIfExists(name + "_a"s)) {
return AssertionFailure() << "Cannot unmap " << name << "_a";
}
@@ -2026,6 +2026,80 @@ TEST_F(SnapshotUpdateTest, LowSpace) {
ASSERT_LT(res.required_size(), 40_MiB);
}
+TEST_F(SnapshotUpdateTest, AddPartition) {
+ // OTA client blindly unmaps all partitions that are possibly mapped.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
+ }
+
+ group_->add_partition_names("dlkm");
+
+ auto dlkm = manifest_.add_partitions();
+ dlkm->set_partition_name("dlkm");
+ dlkm->set_estimate_cow_size(2_MiB);
+ SetSize(dlkm, 3_MiB);
+
+ // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs
+ // fit in super, but not |prd|.
+ constexpr uint64_t partition_size = 3788_KiB;
+ SetSize(sys_, partition_size);
+ SetSize(vnd_, partition_size);
+ SetSize(prd_, partition_size);
+ SetSize(dlkm, partition_size);
+
+ AddOperationForPartitions({sys_, vnd_, prd_, dlkm});
+
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ // Write some data to target partitions.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
+ ASSERT_TRUE(WriteSnapshotAndHash(name));
+ }
+
+ // Assert that source partitions aren't affected.
+ for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name));
+ }
+
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // After reboot, init does first stage mount.
+ auto init = NewManagerForFirstStageMount("_b");
+ ASSERT_NE(init, nullptr);
+
+ ASSERT_TRUE(init->EnsureSnapuserdConnected());
+ init->set_use_first_stage_snapuserd(true);
+
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+ // Check that the target partitions have the same content.
+ std::vector<std::string> partitions = {"sys_b", "vnd_b", "prd_b", "dlkm_b"};
+ for (const auto& name : partitions) {
+ ASSERT_TRUE(IsPartitionUnchanged(name));
+ }
+
+ ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
+ for (const auto& name : partitions) {
+ ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(name + "-user-cow-init"));
+ }
+
+ // Initiate the merge and wait for it to be completed.
+ ASSERT_TRUE(init->InitiateMerge());
+ ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+
+ // Check that the target partitions have the same content after the merge.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name))
+ << "Content of " << name << " changes after the merge";
+ }
+}
+
class AutoKill final {
public:
explicit AutoKill(pid_t pid) : pid_(pid) {}
diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd_worker.cpp
index 682f9da58..defb5bb76 100644
--- a/fs_mgr/libsnapshot/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_worker.cpp
@@ -287,16 +287,36 @@ int WorkerThread::ReadData(sector_t sector, size_t size) {
it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
Snapuserd::compare);
- if (!(it != chunk_vec.end())) {
- SNAP_LOG(ERROR) << "ReadData: Sector " << sector << " not found in chunk_vec";
- return -1;
+ bool read_end_of_device = false;
+ if (it == chunk_vec.end()) {
+ // |-------|-------|-------|
+ // 0 1 2 3
+ //
+ // Block 0 - op 1
+ // Block 1 - op 2
+ // Block 2 - op 3
+ //
+ // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.
+ //
+ // Each block is 4k bytes. Thus, the last block will span 8 sectors
+ // ranging till block 3 (However, block 3 won't be in chunk_vec as
+ // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector
+ // spanning between block 2 and block 3, we need to step back
+ // and get hold of the last element.
+ //
+ // Additionally, dm-snapshot makes sure that I/O request beyond block 3
+ // will not be routed to the daemon. Hence, it is safe to assume that
+ // if a sector is not available in the chunk_vec, the I/O falls in the
+ // end of region.
+ it = std::prev(chunk_vec.end());
+ read_end_of_device = true;
}
// We didn't find the required sector; hence find the previous sector
// as lower_bound will gives us the value greater than
// the requested sector
if (it->first != sector) {
- if (it != chunk_vec.begin()) {
+ if (it != chunk_vec.begin() && !read_end_of_device) {
--it;
}
diff --git a/init/init.cpp b/init/init.cpp
index 28e100486..6dcf4a30b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -27,6 +27,7 @@
#include <sys/mount.h>
#include <sys/signalfd.h>
#include <sys/types.h>
+#include <sys/utsname.h>
#include <unistd.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
@@ -569,6 +570,19 @@ static void SetUsbController() {
}
}
+/// Set ro.kernel.version property to contain the major.minor pair as returned
+/// by uname(2).
+static void SetKernelVersion() {
+ struct utsname uts;
+ unsigned int major, minor;
+
+ if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
+ LOG(ERROR) << "Could not parse the kernel version from uname";
+ return;
+ }
+ SetProperty("ro.kernel.version", android::base::StringPrintf("%u.%u", major, minor));
+}
+
static void HandleSigtermSignal(const signalfd_siginfo& siginfo) {
if (siginfo.ssi_pid != 0) {
// Drop any userspace SIGTERM requests.
@@ -854,6 +868,7 @@ int SecondStageMain(int argc, char** argv) {
export_oem_lock_status();
MountHandler mount_handler(&epoll);
SetUsbController();
+ SetKernelVersion();
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
diff --git a/libprocessgroup/profiles/task_profiles_28.json b/libprocessgroup/profiles/task_profiles_28.json
index 9f8378590..56053e05a 100644
--- a/libprocessgroup/profiles/task_profiles_28.json
+++ b/libprocessgroup/profiles/task_profiles_28.json
@@ -40,6 +40,19 @@
]
},
{
+ "Name": "ServicePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
"Name": "HighPerformance",
"Actions": [
{
diff --git a/libprocessgroup/profiles/task_profiles_29.json b/libprocessgroup/profiles/task_profiles_29.json
index 9f8378590..52279b872 100644
--- a/libprocessgroup/profiles/task_profiles_29.json
+++ b/libprocessgroup/profiles/task_profiles_29.json
@@ -53,6 +53,19 @@
]
},
{
+ "Name": "ServicePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
"Name": "MaxPerformance",
"Actions": [
{
diff --git a/libprocessgroup/profiles/task_profiles_30.json b/libprocessgroup/profiles/task_profiles_30.json
index 9f8378590..56053e05a 100644
--- a/libprocessgroup/profiles/task_profiles_30.json
+++ b/libprocessgroup/profiles/task_profiles_30.json
@@ -40,6 +40,19 @@
]
},
{
+ "Name": "ServicePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
"Name": "HighPerformance",
"Actions": [
{
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 540dcf49d..6e293c741 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -86,8 +86,10 @@ struct thread_data_t {
// A new thread will be in its parent's sched group by default,
// so we just need to handle the background case.
+ // currently set to system_background group which is different
+ // from background group for app.
if (prio >= ANDROID_PRIORITY_BACKGROUND) {
- SetTaskProfiles(0, {"SCHED_SP_BACKGROUND"}, true);
+ SetTaskProfiles(0, {"SCHED_SP_SYSTEM"}, true);
}
if (name) {
@@ -313,7 +315,7 @@ int androidSetThreadPriority(pid_t tid, int pri)
}
if (pri >= ANDROID_PRIORITY_BACKGROUND) {
- rc = SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
+ rc = SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1;
} else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
SchedPolicy policy = SP_FOREGROUND;
// Change to the sched policy group of the process.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 637cab938..8272654c7 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -607,9 +607,23 @@ on late-fs
# Load trusted keys from dm-verity protected partitions
exec -- /system/bin/fsverity_init --load-verified-keys
+# Only enable the bootreceiver tracing instance for kernels 5.10 and above.
+on late-fs && property:ro.kernel.version=4.9
+ setprop bootreceiver.enable 0
+on late-fs && property:ro.kernel.version=4.14
+ setprop bootreceiver.enable 0
+on late-fs && property:ro.kernel.version=4.19
+ setprop bootreceiver.enable 0
+on late-fs && property:ro.kernel.version=5.4
+ setprop bootreceiver.enable 0
+on late-fs
+ # Bootreceiver tracing instance is enabled by default.
+ setprop bootreceiver.enable ${bootreceiver.enable:-1}
+
+on property:ro.product.cpu.abilist64=* && property:bootreceiver.enable=1
# Set up a tracing instance for system_server to monitor error_report_end events.
# These are sent by kernel tools like KASAN and KFENCE when a memory corruption
- # is detected.
+ # is detected. This is only needed for 64-bit systems.
mkdir /sys/kernel/tracing/instances/bootreceiver 0700 system system
restorecon_recursive /sys/kernel/tracing/instances/bootreceiver
write /sys/kernel/tracing/instances/bootreceiver/buffer_size_kb 1
diff --git a/trusty/storage/interface/include/trusty/interface/storage.h b/trusty/storage/interface/include/trusty/interface/storage.h
index b196d88b3..3f1dcb8c6 100644
--- a/trusty/storage/interface/include/trusty/interface/storage.h
+++ b/trusty/storage/interface/include/trusty/interface/storage.h
@@ -112,26 +112,30 @@ enum storage_file_open_flag {
/**
* enum storage_msg_flag - protocol-level flags in struct storage_msg
- * @STORAGE_MSG_FLAG_BATCH: if set, command belongs to a batch transaction.
- * No response will be sent by the server until
- * it receives a command with this flag unset, at
- * which point a cummulative result for all messages
- * sent with STORAGE_MSG_FLAG_BATCH will be sent.
- * This is only supported by the non-secure disk proxy
- * server.
- * @STORAGE_MSG_FLAG_PRE_COMMIT: if set, indicates that server need to commit
- * pending changes before processing this message.
- * @STORAGE_MSG_FLAG_POST_COMMIT: if set, indicates that server need to commit
- * pending changes after processing this message.
- * @STORAGE_MSG_FLAG_TRANSACT_COMPLETE: if set, indicates that server need to commit
- * current transaction after processing this message.
- * It is an alias for STORAGE_MSG_FLAG_POST_COMMIT.
+ * @STORAGE_MSG_FLAG_BATCH: if set, command belongs to a batch transaction.
+ * No response will be sent by the server until
+ * it receives a command with this flag unset, at
+ * which point a cumulative result for all messages
+ * sent with STORAGE_MSG_FLAG_BATCH will be sent.
+ * This is only supported by the non-secure disk proxy
+ * server.
+ * @STORAGE_MSG_FLAG_PRE_COMMIT: if set, indicates that server need to commit
+ * pending changes before processing this message.
+ * @STORAGE_MSG_FLAG_POST_COMMIT: if set, indicates that server need to commit
+ * pending changes after processing this message.
+ * @STORAGE_MSG_FLAG_TRANSACT_COMPLETE: if set, indicates that server need to commit
+ * current transaction after processing this message.
+ * It is an alias for STORAGE_MSG_FLAG_POST_COMMIT.
+ * @STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT: if set, indicates that server needs to ensure
+ * that there is not a pending checkpoint for
+ * userdata before processing this message.
*/
enum storage_msg_flag {
- STORAGE_MSG_FLAG_BATCH = 0x1,
- STORAGE_MSG_FLAG_PRE_COMMIT = 0x2,
- STORAGE_MSG_FLAG_POST_COMMIT = 0x4,
- STORAGE_MSG_FLAG_TRANSACT_COMPLETE = STORAGE_MSG_FLAG_POST_COMMIT,
+ STORAGE_MSG_FLAG_BATCH = 0x1,
+ STORAGE_MSG_FLAG_PRE_COMMIT = 0x2,
+ STORAGE_MSG_FLAG_POST_COMMIT = 0x4,
+ STORAGE_MSG_FLAG_TRANSACT_COMPLETE = STORAGE_MSG_FLAG_POST_COMMIT,
+ STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT = 0x8,
};
/*
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index d67089fb2..38d868508 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -23,6 +23,7 @@ cc_binary {
vendor: true,
srcs: [
+ "checkpoint_handling.cpp",
"ipc.c",
"rpmb.c",
"storage.c",
@@ -30,12 +31,14 @@ cc_binary {
],
shared_libs: [
+ "libbase",
"liblog",
"libhardware_legacy",
],
header_libs: ["libcutils_headers"],
static_libs: [
+ "libfstab",
"libtrustystorageinterface",
"libtrusty",
],
diff --git a/trusty/storage/proxy/checkpoint_handling.cpp b/trusty/storage/proxy/checkpoint_handling.cpp
new file mode 100644
index 000000000..6c2fd363e
--- /dev/null
+++ b/trusty/storage/proxy/checkpoint_handling.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "checkpoint_handling.h"
+#include "log.h"
+
+#include <fstab/fstab.h>
+#include <cstring>
+#include <string>
+
+namespace {
+
+bool checkpointingDoneForever = false;
+
+} // namespace
+
+int is_data_checkpoint_active(bool* active) {
+ if (!active) {
+ ALOGE("active out parameter is null");
+ return 0;
+ }
+
+ *active = false;
+
+ if (checkpointingDoneForever) {
+ return 0;
+ }
+
+ android::fs_mgr::Fstab procMounts;
+ bool success = android::fs_mgr::ReadFstabFromFile("/proc/mounts", &procMounts);
+ if (!success) {
+ ALOGE("Could not parse /proc/mounts\n");
+ /* Really bad. Tell the caller to abort the write. */
+ return -1;
+ }
+
+ android::fs_mgr::FstabEntry* dataEntry =
+ android::fs_mgr::GetEntryForMountPoint(&procMounts, "/data");
+ if (dataEntry == NULL) {
+ ALOGE("/data is not mounted yet\n");
+ return 0;
+ }
+
+ /* We can't handle e.g., ext4. Nothing we can do about it for now. */
+ if (dataEntry->fs_type != "f2fs") {
+ ALOGW("Checkpoint status not supported for filesystem %s\n", dataEntry->fs_type.c_str());
+ checkpointingDoneForever = true;
+ return 0;
+ }
+
+ /*
+ * The data entry looks like "... blah,checkpoint=disable:0,blah ...".
+ * checkpoint=disable means checkpointing is on (yes, arguably reversed).
+ */
+ size_t checkpointPos = dataEntry->fs_options.find("checkpoint=disable");
+ if (checkpointPos == std::string::npos) {
+ /* Assumption is that once checkpointing turns off, it stays off */
+ checkpointingDoneForever = true;
+ } else {
+ *active = true;
+ }
+
+ return 0;
+}
diff --git a/trusty/storage/proxy/checkpoint_handling.h b/trusty/storage/proxy/checkpoint_handling.h
new file mode 100644
index 000000000..f1bf27c8d
--- /dev/null
+++ b/trusty/storage/proxy/checkpoint_handling.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * is_data_checkpoint_active() - Check for an active, uncommitted checkpoint of
+ * /data. If a checkpoint is active, storage should not commit any
+ * rollback-protected writes to /data.
+ * @active: Out parameter that will be set to the result of the check.
+ *
+ * Return: 0 if active was set and is valid, non-zero otherwise.
+ */
+int is_data_checkpoint_active(bool* active);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index e23094183..c690a2876 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -26,6 +26,7 @@
#include <cutils/android_filesystem_config.h>
+#include "checkpoint_handling.h"
#include "ipc.h"
#include "log.h"
#include "rpmb.h"
@@ -130,6 +131,21 @@ static int handle_req(struct storage_msg* msg, const void* req, size_t req_len)
}
}
+ if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT) {
+ bool is_checkpoint_active = false;
+
+ rc = is_data_checkpoint_active(&is_checkpoint_active);
+ if (rc != 0) {
+ ALOGE("is_data_checkpoint_active failed in an unexpected way. Aborting.\n");
+ msg->result = STORAGE_ERR_GENERIC;
+ return ipc_respond(msg, NULL, 0);
+ } else if (is_checkpoint_active) {
+ ALOGE("Checkpoint in progress, dropping write ...\n");
+ msg->result = STORAGE_ERR_GENERIC;
+ return ipc_respond(msg, NULL, 0);
+ }
+ }
+
switch (msg->cmd) {
case STORAGE_FILE_DELETE:
rc = storage_file_delete(msg, req, req_len);
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index b59fb67f6..f059935bf 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -16,7 +16,10 @@
#include <errno.h>
#include <fcntl.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
#include <scsi/sg.h>
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -55,6 +58,17 @@
#define MMC_BLOCK_SIZE 512
/*
+ * Number of retry attempts when an RPMB authenticated write triggers a UNIT
+ * ATTENTION
+ */
+#define UFS_RPMB_WRITE_RETRY_COUNT 1
+/*
+ * Number of retry attempts when an RPMB read operation triggers a UNIT
+ * ATTENTION
+ */
+#define UFS_RPMB_READ_RETRY_COUNT 3
+
+/*
* There should be no timeout for security protocol ioctl call, so we choose a
* large number for timeout.
* 20000 millisecs == 20 seconds
@@ -104,21 +118,62 @@ static enum dev_type dev_type = UNKNOWN_RPMB;
static const char* UFS_WAKE_LOCK_NAME = "ufs_seq_wakelock";
-#ifdef RPMB_DEBUG
-
-static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
+/**
+ * log_buf - Log a byte buffer to the android log.
+ * @priority: One of ANDROID_LOG_* priority levels from android_LogPriority in
+ * android/log.h
+ * @prefix: A null-terminated string that identifies this buffer. Must be less
+ * than 128 bytes.
+ * @buf: Buffer to dump.
+ * @size: Length of @buf in bytes.
+ */
+#define LOG_BUF_SIZE 256
+static int log_buf(int priority, const char* prefix, const uint8_t* buf, size_t size) {
+ int rc;
size_t i;
+ char line[LOG_BUF_SIZE] = {0};
+ char* cur = line;
- printf("%s @%p [%zu]", prefix, buf, size);
+ rc = snprintf(line, LOG_BUF_SIZE, "%s @%p [%zu]", prefix, buf, size);
+ if (rc < 0 || rc >= LOG_BUF_SIZE) {
+ goto err;
+ }
+ cur += rc;
for (i = 0; i < size; i++) {
- if (i && i % 32 == 0) printf("\n%*s", (int)strlen(prefix), "");
- printf(" %02x", buf[i]);
+ if (i % 32 == 0) {
+ /*
+ * Flush the line out to the log after we have printed 32 bytes
+ * (also flushes the header line on the first iteration and sets up
+ * for printing the buffer itself)
+ */
+ LOG_PRI(priority, LOG_TAG, "%s", line);
+ memset(line, 0, LOG_BUF_SIZE);
+ cur = line;
+ /* Shift output over by the length of the prefix */
+ rc = snprintf(line, LOG_BUF_SIZE, "%*s", (int)strlen(prefix), "");
+ if (rc < 0 || rc >= LOG_BUF_SIZE) {
+ goto err;
+ }
+ cur += rc;
+ }
+ rc = snprintf(cur, LOG_BUF_SIZE - (cur - line), "%02x ", buf[i]);
+ if (rc < 0 || rc >= LOG_BUF_SIZE - (cur - line)) {
+ goto err;
+ }
+ cur += rc;
}
- printf("\n");
- fflush(stdout);
-}
+ LOG_PRI(priority, LOG_TAG, "%s", line);
-#endif
+ return 0;
+
+err:
+ if (rc < 0) {
+ return rc;
+ } else {
+ ALOGE("log_buf prefix was too long");
+ return -1;
+ }
+}
static void set_sg_io_hdr(sg_io_hdr_t* io_hdrp, int dxfer_direction, unsigned char cmd_len,
unsigned char mx_sb_len, unsigned int dxfer_len, void* dxferp,
@@ -135,6 +190,137 @@ static void set_sg_io_hdr(sg_io_hdr_t* io_hdrp, int dxfer_direction, unsigned ch
io_hdrp->timeout = TIMEOUT;
}
+/**
+ * enum scsi_result - Results of checking the SCSI status and sense buffer
+ *
+ * @SCSI_RES_OK: SCSI status and sense are good
+ * @SCSI_RES_ERR: SCSI status or sense contain an unhandled error
+ * @SCSI_RES_RETRY: SCSI sense buffer contains a status that indicates that the
+ * command should be retried
+ */
+enum scsi_result {
+ SCSI_RES_OK = 0,
+ SCSI_RES_ERR,
+ SCSI_RES_RETRY,
+};
+
+static enum scsi_result check_scsi_sense(const uint8_t* sense_buf, size_t len) {
+ uint8_t response_code = 0;
+ uint8_t sense_key = 0;
+ uint8_t additional_sense_code = 0;
+ uint8_t additional_sense_code_qualifier = 0;
+ uint8_t additional_length = 0;
+
+ if (!sense_buf || len == 0) {
+ ALOGE("Invalid SCSI sense buffer, length: %zu\n", len);
+ return SCSI_RES_ERR;
+ }
+
+ response_code = 0x7f & sense_buf[0];
+
+ if (response_code < 0x70 || response_code > 0x73) {
+ ALOGE("Invalid SCSI sense response code: %hhu\n", response_code);
+ return SCSI_RES_ERR;
+ }
+
+ if (response_code >= 0x72) {
+ /* descriptor format, SPC-6 4.4.2 */
+ if (len > 1) {
+ sense_key = 0xf & sense_buf[1];
+ }
+ if (len > 2) {
+ additional_sense_code = sense_buf[2];
+ }
+ if (len > 3) {
+ additional_sense_code_qualifier = sense_buf[3];
+ }
+ if (len > 7) {
+ additional_length = sense_buf[7];
+ }
+ } else {
+ /* fixed format, SPC-6 4.4.3 */
+ if (len > 2) {
+ sense_key = 0xf & sense_buf[2];
+ }
+ if (len > 7) {
+ additional_length = sense_buf[7];
+ }
+ if (len > 12) {
+ additional_sense_code = sense_buf[12];
+ }
+ if (len > 13) {
+ additional_sense_code_qualifier = sense_buf[13];
+ }
+ }
+
+ switch (sense_key) {
+ case NO_SENSE:
+ case 0x0f: /* COMPLETED, not present in kernel headers */
+ ALOGD("SCSI success with sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
+ additional_sense_code, additional_sense_code_qualifier);
+ return SCSI_RES_OK;
+ case UNIT_ATTENTION:
+ ALOGD("UNIT ATTENTION with sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
+ additional_sense_code, additional_sense_code_qualifier);
+ if (additional_sense_code == 0x29) {
+ /* POWER ON or RESET condition */
+ return SCSI_RES_RETRY;
+ }
+
+ /* treat this UNIT ATTENTION as an error if we don't recognize it */
+ break;
+ }
+
+ ALOGE("Unexpected SCSI sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
+ additional_sense_code, additional_sense_code_qualifier);
+ log_buf(ANDROID_LOG_ERROR, "sense buffer: ", sense_buf, len);
+ return SCSI_RES_ERR;
+}
+
+static enum scsi_result check_sg_io_hdr(const sg_io_hdr_t* io_hdrp) {
+ if (io_hdrp->status == 0 && io_hdrp->host_status == 0 && io_hdrp->driver_status == 0) {
+ return SCSI_RES_OK;
+ }
+
+ if (io_hdrp->status & 0x01) {
+ ALOGE("SG_IO received unknown status, LSB is set: %hhu", io_hdrp->status);
+ }
+
+ if (io_hdrp->masked_status != GOOD && io_hdrp->sb_len_wr > 0) {
+ enum scsi_result scsi_res = check_scsi_sense(io_hdrp->sbp, io_hdrp->sb_len_wr);
+ if (scsi_res == SCSI_RES_RETRY) {
+ return SCSI_RES_RETRY;
+ } else if (scsi_res != SCSI_RES_OK) {
+ ALOGE("Unexpected SCSI sense. masked_status: %hhu, host_status: %hu, driver_status: "
+ "%hu\n",
+ io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status);
+ return scsi_res;
+ }
+ }
+
+ switch (io_hdrp->masked_status) {
+ case GOOD:
+ break;
+ case CHECK_CONDITION:
+ /* handled by check_sg_sense above */
+ break;
+ default:
+ ALOGE("SG_IO failed with masked_status: %hhu, host_status: %hu, driver_status: %hu\n",
+ io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status);
+ return SCSI_RES_ERR;
+ }
+
+ if (io_hdrp->host_status != 0) {
+ ALOGE("SG_IO failed with host_status: %hu, driver_status: %hu\n", io_hdrp->host_status,
+ io_hdrp->driver_status);
+ }
+
+ if (io_hdrp->resid != 0) {
+ ALOGE("SG_IO resid was non-zero: %d\n", io_hdrp->resid);
+ }
+ return SCSI_RES_ERR;
+}
+
static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
struct {
struct mmc_ioc_multi_cmd multi;
@@ -153,7 +339,7 @@ static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req
mmc_ioc_cmd_set_data((*cmd), write_buf);
#ifdef RPMB_DEBUG
ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
- print_buf("request: ", write_buf, req->reliable_write_size);
+ log_buf(ANDROID_LOG_INFO, "request: ", write_buf, req->reliable_write_size);
#endif
write_buf += req->reliable_write_size;
mmc.multi.num_of_cmds++;
@@ -169,7 +355,7 @@ static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req
mmc_ioc_cmd_set_data((*cmd), write_buf);
#ifdef RPMB_DEBUG
ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
- print_buf("request: ", write_buf, req->write_size);
+ log_buf(ANDROID_LOG_INFO, "request: ", write_buf, req->write_size);
#endif
write_buf += req->write_size;
mmc.multi.num_of_cmds++;
@@ -207,6 +393,8 @@ static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req)
struct sec_proto_cdb out_cdb = {0xB5, 0xEC, 0x00, 0x01, 0x00, 0x00, 0, 0x00, 0x00};
unsigned char sense_buffer[32];
+ bool is_request_write = req->reliable_write_size > 0;
+
wl_rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, UFS_WAKE_LOCK_NAME);
if (wl_rc < 0) {
ALOGE("%s: failed to acquire wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
@@ -215,30 +403,44 @@ static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req)
if (req->reliable_write_size) {
/* Prepare SECURITY PROTOCOL OUT command. */
- out_cdb.length = __builtin_bswap32(req->reliable_write_size);
sg_io_hdr_t io_hdr;
- set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
- req->reliable_write_size, (void*)write_buf, (unsigned char*)&out_cdb,
- sense_buffer);
- rc = ioctl(sg_fd, SG_IO, &io_hdr);
- if (rc < 0) {
- ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
- goto err_op;
- }
+ int retry_count = UFS_RPMB_WRITE_RETRY_COUNT;
+ do {
+ out_cdb.length = __builtin_bswap32(req->reliable_write_size);
+ set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
+ req->reliable_write_size, (void*)write_buf, (unsigned char*)&out_cdb,
+ sense_buffer);
+ rc = ioctl(sg_fd, SG_IO, &io_hdr);
+ if (rc < 0) {
+ ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ goto err_op;
+ }
+ } while (check_sg_io_hdr(&io_hdr) == SCSI_RES_RETRY && retry_count-- > 0);
write_buf += req->reliable_write_size;
}
if (req->write_size) {
/* Prepare SECURITY PROTOCOL OUT command. */
- out_cdb.length = __builtin_bswap32(req->write_size);
sg_io_hdr_t io_hdr;
- set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
- req->write_size, (void*)write_buf, (unsigned char*)&out_cdb, sense_buffer);
- rc = ioctl(sg_fd, SG_IO, &io_hdr);
- if (rc < 0) {
- ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
- goto err_op;
- }
+ /*
+ * We don't retry write response request messages (is_request_write ==
+ * true) because a unit attention condition between the write and
+ * requesting a response means that the device was reset and we can't
+ * get a response to our original write. We can only retry this SG_IO
+ * call when it is the first call in our sequence.
+ */
+ int retry_count = is_request_write ? 0 : UFS_RPMB_READ_RETRY_COUNT;
+ do {
+ out_cdb.length = __builtin_bswap32(req->write_size);
+ set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
+ req->write_size, (void*)write_buf, (unsigned char*)&out_cdb,
+ sense_buffer);
+ rc = ioctl(sg_fd, SG_IO, &io_hdr);
+ if (rc < 0) {
+ ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ goto err_op;
+ }
+ } while (check_sg_io_hdr(&io_hdr) == SCSI_RES_RETRY && retry_count-- > 0);
write_buf += req->write_size;
}
@@ -252,6 +454,7 @@ static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req)
if (rc < 0) {
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
}
+ check_sg_io_hdr(&io_hdr);
}
err_op:
@@ -353,7 +556,7 @@ int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) {
goto err_response;
}
#ifdef RPMB_DEBUG
- if (req->read_size) print_buf("response: ", read_buf, req->read_size);
+ if (req->read_size) log_buf(ANDROID_LOG_INFO, "response: ", read_buf, req->read_size);
#endif
if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {