1 /*
2 * Copyright (C) 2018 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
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20
21 #include <android-base/logging.h>
22 #include <android-base/unique_fd.h>
23
24 #include "meminfo_private.h"
25
26 using unique_fd = ::android::base::unique_fd;
27
28 namespace android {
29 namespace meminfo {
30
pfn_to_idle_bitmap_offset(uint64_t pfn)31 static inline off64_t pfn_to_idle_bitmap_offset(uint64_t pfn) {
32 return static_cast<off64_t>((pfn >> 6) << 3);
33 }
34
is_page_size_emulated()35 static bool is_page_size_emulated() {
36 #if defined (__x86_64__)
37 return getpagesize() != 4096;
38 #else
39 return false;
40 #endif
41 }
42
InitPageAcct(bool pageidle_enable)43 bool PageAcct::InitPageAcct(bool pageidle_enable) {
44 if (is_page_size_emulated()) {
45 return true;
46 }
47
48 if (pageidle_enable && !PageAcct::KernelHasPageIdle()) {
49 LOG(ERROR) << "Idle page tracking is not supported by the kernel";
50 return false;
51 }
52
53 if (kpagecount_fd_ < 0) {
54 unique_fd count_fd(TEMP_FAILURE_RETRY(open("/proc/kpagecount", O_RDONLY | O_CLOEXEC)));
55 if (count_fd < 0) {
56 PLOG(ERROR) << "Failed to open /proc/kpagecount";
57 return false;
58 }
59 kpagecount_fd_ = std::move(count_fd);
60 }
61
62 if (kpageflags_fd_ < 0) {
63 unique_fd flags_fd(TEMP_FAILURE_RETRY(open("/proc/kpageflags", O_RDONLY | O_CLOEXEC)));
64 if (flags_fd < 0) {
65 PLOG(ERROR) << "Failed to open /proc/kpageflags";
66 return false;
67 }
68 kpageflags_fd_ = std::move(flags_fd);
69 }
70
71 if (pageidle_enable && pageidle_fd_ < 0) {
72 unique_fd idle_fd(
73 TEMP_FAILURE_RETRY(open("/sys/kernel/mm/page_idle/bitmap", O_RDWR | O_CLOEXEC)));
74 if (idle_fd < 0) {
75 PLOG(ERROR) << "Failed to open page idle bitmap";
76 return false;
77 }
78 pageidle_fd_ = std::move(idle_fd);
79 }
80
81 return true;
82 }
83
PageFlags(uint64_t pfn,uint64_t * flags)84 bool PageAcct::PageFlags(uint64_t pfn, uint64_t* flags) {
85 if (!flags) return false;
86
87 if (is_page_size_emulated()) {
88 *flags = 0;
89 return true;
90 }
91
92 if (kpageflags_fd_ < 0) {
93 if (!InitPageAcct()) return false;
94 }
95
96 if (pread64(kpageflags_fd_, flags, sizeof(uint64_t), pfn * sizeof(uint64_t)) !=
97 sizeof(uint64_t)) {
98 PLOG(ERROR) << "Failed to read page flags for page " << pfn;
99 return false;
100 }
101 return true;
102 }
103
PageMapCount(uint64_t pfn,uint64_t * mapcount)104 bool PageAcct::PageMapCount(uint64_t pfn, uint64_t* mapcount) {
105 if (!mapcount) return false;
106
107 if (is_page_size_emulated()) {
108 *mapcount = 1;
109 return true;
110 }
111
112 if (kpagecount_fd_ < 0) {
113 if (!InitPageAcct()) return false;
114 }
115
116 if (pread64(kpagecount_fd_, mapcount, sizeof(uint64_t), pfn * sizeof(uint64_t)) !=
117 sizeof(uint64_t)) {
118 PLOG(ERROR) << "Failed to read map count for page " << pfn;
119 return false;
120 }
121 return true;
122 }
123
IsPageIdle(uint64_t pfn)124 int PageAcct::IsPageIdle(uint64_t pfn) {
125 if (is_page_size_emulated()) {
126 return 0;
127 }
128
129 if (pageidle_fd_ < 0) {
130 if (!InitPageAcct(true)) return -EOPNOTSUPP;
131 }
132
133 int idle_status = MarkPageIdle(pfn);
134 if (idle_status) return idle_status;
135
136 return GetPageIdle(pfn);
137 }
138
MarkPageIdle(uint64_t pfn) const139 int PageAcct::MarkPageIdle(uint64_t pfn) const {
140 off64_t offset = pfn_to_idle_bitmap_offset(pfn);
141 // set the bit corresponding to page frame
142 uint64_t idle_bits = 1ULL << (pfn % 64);
143
144 if (pwrite64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) {
145 PLOG(ERROR) << "Failed to write page idle bitmap for page " << pfn;
146 return -errno;
147 }
148
149 return 0;
150 }
151
GetPageIdle(uint64_t pfn) const152 int PageAcct::GetPageIdle(uint64_t pfn) const {
153 off64_t offset = pfn_to_idle_bitmap_offset(pfn);
154 uint64_t idle_bits;
155
156 if (pread64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) != sizeof(uint64_t)) {
157 PLOG(ERROR) << "Failed to read page idle bitmap for page " << pfn;
158 return -errno;
159 }
160
161 return !!(idle_bits & (1ULL << (pfn % 64)));
162 }
163
164 // Public methods
page_present(uint64_t pagemap_val)165 bool page_present(uint64_t pagemap_val) {
166 return PAGE_PRESENT(pagemap_val);
167 }
168
page_swapped(uint64_t pagemap_val)169 bool page_swapped(uint64_t pagemap_val) {
170 return PAGE_SWAPPED(pagemap_val);
171 }
172
page_pfn(uint64_t pagemap_val)173 uint64_t page_pfn(uint64_t pagemap_val) {
174 return PAGE_PFN(pagemap_val);
175 }
176
177 } // namespace meminfo
178 } // namespace android
179