Configure backing device max_ratio for FUSE filesystems. am: a485006ab1 am: 5ed648d098 am: 6589ae36c3
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/vold/+/12024019
Change-Id: I20555c69735747460b0ffe137a116b566ef5b158
diff --git a/Utils.cpp b/Utils.cpp
index 6208efd..a9b7440 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -1386,22 +1386,54 @@
return OK;
}
+// Gets the sysfs path for parameters of the backing device info (bdi)
+static std::string getBdiPathForMount(const std::string& mount) {
+ // First figure out MAJOR:MINOR of mount. Simplest way is to stat the path.
+ struct stat info;
+ if (stat(mount.c_str(), &info) != 0) {
+ PLOG(ERROR) << "Failed to stat " << mount;
+ return "";
+ }
+ unsigned int maj = major(info.st_dev);
+ unsigned int min = minor(info.st_dev);
+
+ return StringPrintf("/sys/class/bdi/%u:%u", maj, min);
+}
+
+// Configures max_ratio for the FUSE filesystem.
+void ConfigureMaxDirtyRatioForFuse(const std::string& fuse_mount, unsigned int max_ratio) {
+ LOG(INFO) << "Configuring max_ratio of " << fuse_mount << " fuse filesystem to " << max_ratio;
+ if (max_ratio > 100) {
+ LOG(ERROR) << "Invalid max_ratio: " << max_ratio;
+ return;
+ }
+ std::string fuseBdiPath = getBdiPathForMount(fuse_mount);
+ if (fuseBdiPath == "") {
+ return;
+ }
+ std::string max_ratio_file = StringPrintf("%s/max_ratio", fuseBdiPath.c_str());
+ unique_fd fd(TEMP_FAILURE_RETRY(open(max_ratio_file.c_str(), O_WRONLY | O_CLOEXEC)));
+ if (fd.get() == -1) {
+ PLOG(ERROR) << "Failed to open " << max_ratio_file;
+ return;
+ }
+ LOG(INFO) << "Writing " << max_ratio << " to " << max_ratio_file;
+ if (!WriteStringToFd(std::to_string(max_ratio), fd)) {
+ PLOG(ERROR) << "Failed to write to " << max_ratio_file;
+ }
+}
+
// Configures read ahead property of the fuse filesystem with the mount point |fuse_mount| by
// writing |read_ahead_kb| to the /sys/class/bdi/MAJOR:MINOR/read_ahead_kb.
void ConfigureReadAheadForFuse(const std::string& fuse_mount, size_t read_ahead_kb) {
LOG(INFO) << "Configuring read_ahead of " << fuse_mount << " fuse filesystem to "
<< read_ahead_kb << "kb";
- // First figure out MAJOR:MINOR of fuse_mount. Simplest way is to stat the path.
- struct stat info;
- if (stat(fuse_mount.c_str(), &info) != 0) {
- PLOG(ERROR) << "Failed to stat " << fuse_mount;
+ std::string fuseBdiPath = getBdiPathForMount(fuse_mount);
+ if (fuseBdiPath == "") {
return;
}
- unsigned int maj = major(info.st_dev);
- unsigned int min = minor(info.st_dev);
- LOG(INFO) << fuse_mount << " has major:minor " << maj << ":" << min;
- // We found major:minor of our filesystem, time to configure read ahead!
- std::string read_ahead_file = StringPrintf("/sys/class/bdi/%u:%u/read_ahead_kb", maj, min);
+ // We found the bdi path for our filesystem, time to configure read ahead!
+ std::string read_ahead_file = StringPrintf("%s/read_ahead_kb", fuseBdiPath.c_str());
unique_fd fd(TEMP_FAILURE_RETRY(open(read_ahead_file.c_str(), O_WRONLY | O_CLOEXEC)));
if (fd.get() == -1) {
PLOG(ERROR) << "Failed to open " << read_ahead_file;
diff --git a/Utils.h b/Utils.h
index a1d34b8..04cbac4 100644
--- a/Utils.h
+++ b/Utils.h
@@ -176,6 +176,8 @@
bool writeStringToFile(const std::string& payload, const std::string& filename);
+void ConfigureMaxDirtyRatioForFuse(const std::string& fuse_mount, unsigned int max_ratio);
+
void ConfigureReadAheadForFuse(const std::string& fuse_mount, size_t read_ahead_kb);
status_t MountUserFuse(userid_t user_id, const std::string& absolute_lower_path,
diff --git a/model/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
index 26d9582..db93bc2 100644
--- a/model/EmulatedVolume.cpp
+++ b/model/EmulatedVolume.cpp
@@ -404,6 +404,27 @@
ConfigureReadAheadForFuse(GetFuseMountPathForUser(user_id, label), 256u);
+ // By default, FUSE has a max_dirty ratio of 1%. This means that out of
+ // all dirty pages in the system, only 1% is allowed to belong to any
+ // FUSE filesystem. The reason this is in place is that FUSE
+ // filesystems shouldn't be trusted by default; a FUSE filesystem could
+ // take up say 100% of dirty pages, and subsequently refuse to write
+ // them back to storage. The kernel will then apply rate-limiting, and
+ // block other tasks from writing. For this particular FUSE filesystem
+ // however, we trust the implementation, because it is a part of the
+ // Android platform. So use the default ratio of 100%.
+ //
+ // The reason we're setting this is that there's a suspicion that the
+ // kernel starts rate-limiting the FUSE filesystem under extreme
+ // memory pressure scenarios. While the kernel will only rate limit if
+ // the writeback can't keep up with the write rate, under extreme
+ // memory pressure the write rate may dip as well, in which case FUSE
+ // writes to a 1% max_ratio filesystem are throttled to an extreme amount.
+ //
+ // To prevent this, just give FUSE 40% max_ratio, meaning it can take
+ // up to 40% of all dirty pages in the system.
+ ConfigureMaxDirtyRatioForFuse(GetFuseMountPathForUser(user_id, label), 40u);
+
// All mounts where successful, disable scope guards
sdcardfs_guard.Disable();
fuse_guard.Disable();
diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp
index 9ca782b..d40e3e3 100644
--- a/model/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -255,6 +255,9 @@
}
ConfigureReadAheadForFuse(GetFuseMountPathForUser(user_id, stableName), 256u);
+
+ // See comment in model/EmulatedVolume.cpp
+ ConfigureMaxDirtyRatioForFuse(GetFuseMountPathForUser(user_id, stableName), 40u);
}
return OK;