[automerger skipped] [storageproxyd] Dump binary buffers to log instead of stderr am: 77e2af18be -s ours
am skip reason: Merged-In Iecc0d4680420bb92c94be628789093888a2f1941 with SHA-1 f647221827 is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/core/+/15983383
Change-Id: I55e606476410febd80dbad2662f6f75c3521d239
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index ee0aa58..9b5d2cd 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -187,11 +187,17 @@
", 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 @@
}
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 d0c89b9..f5ab557 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -413,17 +413,24 @@
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 d09c6e9..bd432bb 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -108,6 +108,7 @@
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::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 @@
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 15882b3..9bf5db1 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -399,6 +399,7 @@
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 0e36da1..4c94da2 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -518,6 +518,13 @@
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 @@
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 @@
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 @@
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 @@
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 6018643..7630efe 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -963,7 +963,7 @@
}
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 @@
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 682f9da..defb5bb 100644
--- a/fs_mgr/libsnapshot/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_worker.cpp
@@ -287,16 +287,36 @@
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/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 48e1641..f059935 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -58,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
@@ -179,8 +190,21 @@
io_hdrp->timeout = TIMEOUT;
}
-/* Returns false if the sense data was valid and no errors were present */
-static bool check_scsi_sense(const uint8_t* sense_buf, size_t len) {
+/**
+ * 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;
@@ -189,14 +213,14 @@
if (!sense_buf || len == 0) {
ALOGE("Invalid SCSI sense buffer, length: %zu\n", len);
- return false;
+ 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 false;
+ return SCSI_RES_ERR;
}
if (response_code >= 0x72) {
@@ -234,18 +258,28 @@
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 true;
+ 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 false;
+ return SCSI_RES_ERR;
}
-static void check_sg_io_hdr(const sg_io_hdr_t* io_hdrp) {
+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;
+ return SCSI_RES_OK;
}
if (io_hdrp->status & 0x01) {
@@ -253,12 +287,14 @@
}
if (io_hdrp->masked_status != GOOD && io_hdrp->sb_len_wr > 0) {
- bool sense_error = check_scsi_sense(io_hdrp->sbp, io_hdrp->sb_len_wr);
- if (sense_error) {
+ 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;
+ return scsi_res;
}
}
@@ -271,7 +307,7 @@
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;
+ return SCSI_RES_ERR;
}
if (io_hdrp->host_status != 0) {
@@ -282,6 +318,7 @@
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) {
@@ -356,6 +393,8 @@
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));
@@ -364,32 +403,44 @@
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;
- }
- check_sg_io_hdr(&io_hdr);
+ 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;
- }
- check_sg_io_hdr(&io_hdr);
+ /*
+ * 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;
}