1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <health-storage-impl/common.h>
17
18 #include <android-base/chrono_utils.h>
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <android-base/strings.h>
22 #include <fstab/fstab.h>
23
24 using ::android::base::ReadFileToString;
25 using ::android::base::Timer;
26 using ::android::base::Trim;
27 using ::android::base::WriteStringToFd;
28 using ::android::base::WriteStringToFile;
29 using ::android::fs_mgr::Fstab;
30 using ::android::fs_mgr::ReadDefaultFstab;
31 using ::android::hardware::health::storage::V1_0::Result;
32
33 namespace android::hardware::health::storage {
34
GetGarbageCollectPath()35 static std::string GetGarbageCollectPath() {
36 Fstab fstab;
37 ReadDefaultFstab(&fstab);
38
39 for (const auto& entry : fstab) {
40 if (!entry.sysfs_path.empty()) {
41 return entry.sysfs_path + "/manual_gc";
42 }
43 }
44
45 return "";
46 }
47
GetWriteBoosterPath()48 static std::string GetWriteBoosterPath() {
49 Fstab fstab;
50 ReadDefaultFstab(&fstab);
51
52 for (const auto& entry : fstab) {
53 if (!entry.sysfs_path.empty()) {
54 return entry.sysfs_path + "/attributes/wb_avail_buf";
55 }
56 }
57
58 return "";
59 }
60
GarbageCollect(uint64_t timeout_seconds)61 Result GarbageCollect(uint64_t timeout_seconds) {
62 std::string gc_path = GetGarbageCollectPath();
63
64 if (gc_path.empty()) {
65 LOG(WARNING) << "Cannot find Dev GC path";
66 return Result::UNKNOWN_ERROR;
67 }
68
69 Result result = Result::SUCCESS;
70 Timer timer;
71 LOG(INFO) << "Start Dev GC on " << gc_path;
72 while (1) {
73 std::string require_gc;
74 if (!ReadFileToString(gc_path, &require_gc)) {
75 PLOG(WARNING) << "Reading manual_gc failed in " << gc_path;
76 result = Result::IO_ERROR;
77 break;
78 }
79 require_gc = Trim(require_gc);
80
81 std::string wb_path = GetWriteBoosterPath();
82 // Let's flush WB till 100% available
83 std::string wb_avail = "0x0000000A";
84 if (!wb_path.empty() && !ReadFileToString(wb_path, &wb_avail)) {
85 PLOG(WARNING) << "Reading wb_avail_buf failed in " << wb_path;
86 }
87 wb_avail = Trim(wb_avail);
88
89 if (require_gc == "disabled") {
90 LOG(DEBUG) << "Disabled Dev GC";
91 break;
92 }
93 if ((require_gc == "" || require_gc == "off") && wb_avail == "0x0000000A") {
94 LOG(DEBUG) << "No more to do Dev GC";
95 break;
96 }
97 LOG(DEBUG) << "Trigger Dev GC on " << gc_path << " having " << require_gc << ", WB on "
98 << wb_path << " having " << wb_avail;
99 if (!WriteStringToFile("1", gc_path)) {
100 PLOG(WARNING) << "Start Dev GC failed on " << gc_path;
101 result = Result::IO_ERROR;
102 break;
103 }
104 if (timer.duration() >= std::chrono::seconds(timeout_seconds)) {
105 LOG(WARNING) << "Dev GC timeout";
106 // Timeout is not treated as an error. Try next time.
107 break;
108 }
109 sleep(2);
110 }
111 LOG(INFO) << "Stop Dev GC on " << gc_path;
112 if (!WriteStringToFile("0", gc_path)) {
113 PLOG(WARNING) << "Stop Dev GC failed on " << gc_path;
114 result = Result::IO_ERROR;
115 }
116
117 return result;
118 }
119
DebugDump(int fd)120 void DebugDump(int fd) {
121 std::stringstream output;
122
123 std::string path = GetGarbageCollectPath();
124 if (path.empty()) {
125 output << "Cannot find Dev GC path";
126 } else {
127 std::string require_gc;
128
129 if (ReadFileToString(path, &require_gc)) {
130 output << path << ":" << require_gc << std::endl;
131 }
132
133 if (WriteStringToFile("0", path)) {
134 output << "stop success" << std::endl;
135 }
136 }
137 std::string wb_path = GetWriteBoosterPath();
138 if (wb_path.empty()) {
139 output << "Cannot find Dev WriteBooster path";
140 } else {
141 std::string wb_available;
142
143 if (ReadFileToString(wb_path, &wb_available)) {
144 output << wb_path << ":" << wb_available << std::endl;
145 }
146 }
147 if (!WriteStringToFd(output.str(), fd)) {
148 PLOG(WARNING) << "debug: cannot write to fd";
149 }
150
151 fsync(fd);
152 }
153
154 } // namespace android::hardware::health::storage
155