• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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