Add APIs to manage WriteBooster buffer
Five new APIs are introduced to manage and flush
UFS WriteBooster buffer.
Bug: 349648148
Test: StorageManager.*WriteBooster*
Change-Id: I2f82a2d8b08d83a63cbc3da4d9512792b0d472ba
Signed-off-by: Daniel Lee <chullee@google.com>
diff --git a/Android.bp b/Android.bp
index 1eeb1a1..f1221eb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -143,6 +143,7 @@
"VoldNativeServiceValidation.cpp",
"VoldUtil.cpp",
"VolumeManager.cpp",
+ "WriteBooster.cpp",
"cryptfs.cpp",
"fs/Exfat.cpp",
"fs/Ext4.cpp",
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index 3784487..aa9d842 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -40,6 +40,7 @@
#include "VoldNativeServiceValidation.h"
#include "VoldUtil.h"
#include "VolumeManager.h"
+#include "WriteBooster.h"
#include "cryptfs.h"
#include "incfs.h"
@@ -512,6 +513,45 @@
return Ok();
}
+binder::Status VoldNativeService::getWriteBoosterBufferSize(int32_t* _aidl_return) {
+ ENFORCE_SYSTEM_OR_ROOT;
+
+ *_aidl_return = GetWriteBoosterBufferSize();
+ return Ok();
+}
+
+binder::Status VoldNativeService::getWriteBoosterBufferAvailablePercent(int32_t* _aidl_return) {
+ ENFORCE_SYSTEM_OR_ROOT;
+ ACQUIRE_LOCK;
+
+ *_aidl_return = GetWriteBoosterBufferAvailablePercent();
+ return Ok();
+}
+
+binder::Status VoldNativeService::setWriteBoosterBufferFlush(bool enable, bool* _aidl_return) {
+ ENFORCE_SYSTEM_OR_ROOT;
+ ACQUIRE_LOCK;
+
+ *_aidl_return = SetWriteBoosterBufferFlush(enable);
+ return Ok();
+}
+
+binder::Status VoldNativeService::setWriteBoosterBufferOn(bool enable, bool* _aidl_return) {
+ ENFORCE_SYSTEM_OR_ROOT;
+ ACQUIRE_LOCK;
+
+ *_aidl_return = SetWriteBoosterBufferOn(enable);
+ return Ok();
+}
+
+binder::Status VoldNativeService::getWriteBoosterLifeTimeEstimate(int32_t* _aidl_return) {
+ ENFORCE_SYSTEM_OR_ROOT;
+ ACQUIRE_LOCK;
+
+ *_aidl_return = GetWriteBoosterLifeTimeEstimate();
+ return Ok();
+}
+
binder::Status VoldNativeService::setGCUrgentPace(int32_t neededSegments,
int32_t minSegmentThreshold,
float dirtyReclaimRate, float reclaimWeight,
diff --git a/VoldNativeService.h b/VoldNativeService.h
index a5253c0..2d0613c 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -165,6 +165,12 @@
binder::Status destroyDsuMetadataKey(const std::string& dsuSlot) override;
binder::Status getStorageSize(int64_t* storageSize) override;
+
+ binder::Status getWriteBoosterBufferSize(int32_t* _aidl_return);
+ binder::Status getWriteBoosterBufferAvailablePercent(int32_t* _aidl_return);
+ binder::Status setWriteBoosterBufferFlush(bool enable, bool* _aidl_return);
+ binder::Status setWriteBoosterBufferOn(bool enable, bool* _aidl_return);
+ binder::Status getWriteBoosterLifeTimeEstimate(int32_t* _aidl_return);
};
} // namespace vold
diff --git a/VoldUtil.cpp b/VoldUtil.cpp
index 082f743..c87b41d 100644
--- a/VoldUtil.cpp
+++ b/VoldUtil.cpp
@@ -15,5 +15,73 @@
*/
#include "VoldUtil.h"
+#include "Utils.h"
+
+#include <libdm/dm.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+using android::base::Basename;
+using android::base::Realpath;
+using namespace android::dm;
android::fs_mgr::Fstab fstab_default;
+
+static std::string GetUfsHostControllerSysfsPathOnce() {
+ android::fs_mgr::FstabEntry* entry =
+ android::fs_mgr::GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT);
+ if (entry == nullptr) {
+ LOG(ERROR) << "No mount point entry for " << DATA_MNT_POINT;
+ return "";
+ }
+
+ // Handle symlinks.
+ std::string real_path;
+ if (!Realpath(entry->blk_device, &real_path)) {
+ real_path = entry->blk_device;
+ }
+
+ // Handle logical volumes.
+ android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+ for (;;) {
+ std::optional<std::string> parent = dm.GetParentBlockDeviceByPath(real_path);
+ if (!parent.has_value()) break;
+ real_path = *parent;
+ }
+
+ std::string path = "/sys/class/block/" + Basename(real_path);
+
+ // Walk up the sysfs directory tree from the partition (e.g, /sys/class/block/sda34)
+ // or from the disk (e.g., /sys/class/block/sda) to reach the UFS host controller's directory
+ // (e.g., /sys/class/block/sda34/../device/../../.. --> /sys/devices/platform/00000000.ufs).
+ if (android::vold::pathExists(path + "/../device")) {
+ path += "/../device/../../..";
+ } else if (android::vold::pathExists(path + "/device")) {
+ path += "/device/../../..";
+ } else {
+ LOG(WARNING) << "Failed to get sysfs_path for user data partition";
+ return "";
+ }
+
+ // Verify the block device is UFS by checking for the presence of "uic_link_state",
+ // which is UFS interconnect layer link state.
+ // If not UFS, return an empty path.
+ if (!android::vold::pathExists(path + "/uic_link_state")) {
+ LOG(ERROR) << "The block device (" << Basename(real_path) << ") of " << DATA_MNT_POINT
+ << " is not UFS.";
+ return "";
+ }
+
+ LOG(DEBUG) << "The sysfs directory for the ufs host controller is found at " << path;
+ return path;
+}
+
+// get sysfs directory for the ufs host controller containing userdata
+// returns an empty string on failures.
+std::string GetUfsHostControllerSysfsPath() {
+ static std::string ufshc_sysfs_path = GetUfsHostControllerSysfsPathOnce();
+ return ufshc_sysfs_path;
+}
diff --git a/VoldUtil.h b/VoldUtil.h
index ce6b411..9fb9e96 100644
--- a/VoldUtil.h
+++ b/VoldUtil.h
@@ -21,3 +21,5 @@
extern android::fs_mgr::Fstab fstab_default;
#define DATA_MNT_POINT "/data"
+
+std::string GetUfsHostControllerSysfsPath();
diff --git a/WriteBooster.cpp b/WriteBooster.cpp
new file mode 100644
index 0000000..1d5c1bc
--- /dev/null
+++ b/WriteBooster.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2024 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 "WriteBooster.h"
+#include "Utils.h"
+#include "VoldUtil.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/result.h>
+#include <android-base/strings.h>
+
+using android::base::ReadFileToString;
+using android::base::WriteStringToFile;
+
+namespace android {
+namespace vold {
+
+template <typename T>
+static android::base::Result<T> readHexValue(const std::string_view path) {
+ std::string sysfs_path = GetUfsHostControllerSysfsPath();
+ if (sysfs_path.empty()) {
+ return android::base::Error();
+ }
+
+ std::string fullpath = sysfs_path + "/" + std::string(path);
+ std::string s;
+
+ if (!ReadFileToString(fullpath, &s)) {
+ PLOG(WARNING) << "Reading failed for " << fullpath;
+ return android::base::Error();
+ }
+
+ s = android::base::Trim(s);
+ T out;
+ if (!android::base::ParseUint(s, &out)) {
+ PLOG(WARNING) << "Parsing of " << fullpath << " failed. Content: " << s;
+ return android::base::Error();
+ }
+
+ return out;
+}
+
+int32_t GetWriteBoosterBufferSize() {
+ /* wb_cur_buf: in unit of allocation_unit_size (field width is 4 bytes)
+ * allocation_unit_size: in unit of segments (field width is 1 bytes)
+ * segment_size: in unit of 512 bytes (field width is 4 bytes)
+ * raw_device_capacity: in unit of 512 bytes (field width is 8 bytes)
+ */
+ auto allocation_unit_size = readHexValue<uint8_t>("geometry_descriptor/allocation_unit_size");
+ if (!allocation_unit_size.ok()) {
+ return -1;
+ }
+
+ auto segment_size = readHexValue<uint32_t>("geometry_descriptor/segment_size");
+ if (!segment_size.ok()) {
+ return -1;
+ }
+
+ auto wb_cur_buf = readHexValue<uint32_t>("attributes/wb_cur_buf");
+ if (!wb_cur_buf.ok()) {
+ return -1;
+ }
+
+ auto raw_device_capacity = readHexValue<uint64_t>("geometry_descriptor/raw_device_capacity");
+ if (!raw_device_capacity.ok()) {
+ return -1;
+ }
+
+ if (allocation_unit_size.value() == 0) {
+ LOG(DEBUG) << "Zero allocation_unit_size is invalid.";
+ return -1;
+ }
+
+ if (segment_size.value() == 0) {
+ LOG(DEBUG) << "Zero segment_size is invalid.";
+ return -1;
+ }
+
+ uint64_t wb_cur_buf_allocation_units =
+ static_cast<uint64_t>(wb_cur_buf.value()) * segment_size.value();
+
+ if (wb_cur_buf_allocation_units >
+ (raw_device_capacity.value() / allocation_unit_size.value())) {
+ LOG(DEBUG) << "invalid wb_cur_buff > raw_device_capacity ";
+ return -1;
+ }
+
+ /* The allocation_unit_size is represented in the number of sectors.
+ * Since the sector size is 512 bytes, the allocation_unit_size in MiB can be calculated as
+ * follows: allocation_unit_size in MiB = (allocation_unit_size * 512) / 1024 / 1024 =
+ * allocation_unit_size / 2048
+ */
+ uint64_t wb_cur_buf_mib = wb_cur_buf_allocation_units * allocation_unit_size.value() / 2048ULL;
+
+ if (wb_cur_buf_mib > INT32_MAX) {
+ LOG(DEBUG) << "wb_cur_buff overflow";
+ return -1;
+ }
+
+ /* return in unit of MiB */
+ return static_cast<int32_t>(wb_cur_buf_mib);
+}
+
+/*
+ * Returns the WriteBooster buffer's remaining capacity as a percentage (0-100).
+ */
+int32_t GetWriteBoosterBufferAvailablePercent() {
+ /*
+ * wb_avail_buf is in unit of 10% granularity.
+ * 00h: 0% buffer remains.
+ * 01h~09h: 10%~90% buffer remains
+ * 0Ah: 100% buffer remains
+ * Others : Reserved
+ */
+ auto out = readHexValue<uint8_t>("attributes/wb_avail_buf");
+ if (!out.ok()) {
+ return -1;
+ }
+
+ if (out.value() > 10) {
+ PLOG(WARNING) << "Invalid wb_avail_buf (" << out.value() << ")";
+ return -1;
+ }
+
+ return static_cast<int32_t>(out.value() * 10);
+}
+
+bool SetWriteBoosterBufferFlush(bool enable) {
+ std::string path = GetUfsHostControllerSysfsPath();
+ if (path.empty()) {
+ return false;
+ }
+
+ path += "/enable_wb_buf_flush";
+
+ std::string s = enable ? "1" : "0";
+
+ LOG(DEBUG) << "Toggle WriteBoosterBufferFlush to " << s;
+ if (!WriteStringToFile(s, std::string(path))) {
+ PLOG(WARNING) << "Failed to set WriteBoosterBufferFlush to " << s << " on " << path;
+ return false;
+ }
+ return true;
+}
+
+bool SetWriteBoosterBufferOn(bool enable) {
+ std::string path = GetUfsHostControllerSysfsPath();
+ if (path.empty()) {
+ return false;
+ }
+
+ path += "/wb_on";
+
+ std::string s = enable ? "1" : "0";
+
+ LOG(DEBUG) << "Toggle WriteBoosterBufferOn to " << s;
+ if (!WriteStringToFile(s, std::string(path))) {
+ PLOG(WARNING) << "Failed to set WriteBoosterBufferOn to " << s << " on " << path;
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Returns WriteBooster buffer lifetime as a percentage (0-100).
+ *
+ */
+int32_t GetWriteBoosterLifeTimeEstimate() {
+ /*
+ * wb_life_time_est returns as follows:
+ * 00h: Information not available (WriteBooster Buffer is disabled)
+ * 01h: 0% - 10% WriteBooster Buffer life time used
+ * 02h-09h: 10% - 90% WriteBooster Buffer life time used
+ * 0Ah: 90% - 100% WriteBooster Buffer life time used
+ * 0Bh: Exceeded its maximum estimated WriteBooster Buffer life time
+ * (write commands are processed as if WriteBooster feature was
+ * disabled)
+ * Others: Reserved
+ */
+ auto out = readHexValue<uint8_t>("attributes/wb_life_time_est");
+ if (!out.ok()) {
+ return -1;
+ }
+
+ if (out.value() == 0) {
+ PLOG(WARNING) << "WriteBooster is disabled.";
+ return -1;
+ }
+
+ if (out.value() > 11) {
+ PLOG(WARNING) << "Invalid wb_life_time_est (" << out.value() << ")";
+ return -1;
+ }
+
+ return static_cast<int32_t>(10 * (out.value() - 1));
+}
+
+} // namespace vold
+} // namespace android
diff --git a/WriteBooster.h b/WriteBooster.h
new file mode 100644
index 0000000..6aa9799
--- /dev/null
+++ b/WriteBooster.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#ifndef ANDROID_VOLD_WRITE_BOOSTER_H
+#define ANDROID_VOLD_WRITE_BOOSTER_H
+
+#include <cstdint>
+
+namespace android {
+namespace vold {
+
+int32_t GetWriteBoosterBufferSize();
+int32_t GetWriteBoosterBufferAvailablePercent();
+bool SetWriteBoosterBufferFlush(bool enable);
+bool SetWriteBoosterBufferOn(bool enable);
+int32_t GetWriteBoosterLifeTimeEstimate();
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 810fdad..15213ea 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -143,6 +143,12 @@
// on failure.
int getStorageRemainingLifetime();
+ int getWriteBoosterBufferSize();
+ int getWriteBoosterBufferAvailablePercent();
+ boolean setWriteBoosterBufferFlush(boolean enable);
+ boolean setWriteBoosterBufferOn(boolean enable);
+ int getWriteBoosterLifeTimeEstimate();
+
const int FSTRIM_FLAG_DEEP_TRIM = 1;
const int MOUNT_FLAG_PRIMARY = 1;