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