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 31 static inline off64_t pfn_to_idle_bitmap_offset(uint64_t pfn) { 32 return static_cast<off64_t>((pfn >> 6) << 3); 33 } 34 35 uint64_t pagesize(void) { 36 static uint64_t pagesize = sysconf(_SC_PAGE_SIZE); 37 return pagesize; 38 } 39 40 bool PageAcct::InitPageAcct(bool pageidle_enable) { 41 if (pageidle_enable && !PageAcct::KernelHasPageIdle()) { 42 LOG(ERROR) << "Idle page tracking is not supported by the kernel"; 43 return false; 44 } 45 46 if (kpagecount_fd_ < 0) { 47 unique_fd count_fd(TEMP_FAILURE_RETRY(open("/proc/kpagecount", O_RDONLY | O_CLOEXEC))); 48 if (count_fd < 0) { 49 PLOG(ERROR) << "Failed to open /proc/kpagecount"; 50 return false; 51 } 52 kpagecount_fd_ = std::move(count_fd); 53 } 54 55 if (kpageflags_fd_ < 0) { 56 unique_fd flags_fd(TEMP_FAILURE_RETRY(open("/proc/kpageflags", O_RDONLY | O_CLOEXEC))); 57 if (flags_fd < 0) { 58 PLOG(ERROR) << "Failed to open /proc/kpageflags"; 59 return false; 60 } 61 kpageflags_fd_ = std::move(flags_fd); 62 } 63 64 if (pageidle_enable && pageidle_fd_ < 0) { 65 unique_fd idle_fd( 66 TEMP_FAILURE_RETRY(open("/sys/kernel/mm/page_idle/bitmap", O_RDWR | O_CLOEXEC))); 67 if (idle_fd < 0) { 68 PLOG(ERROR) << "Failed to open page idle bitmap"; 69 return false; 70 } 71 pageidle_fd_ = std::move(idle_fd); 72 } 73 74 return true; 75 } 76 77 bool PageAcct::PageFlags(uint64_t pfn, uint64_t* flags) { 78 if (!flags) return false; 79 80 if (kpageflags_fd_ < 0) { 81 if (!InitPageAcct()) return false; 82 } 83 84 if (pread64(kpageflags_fd_, flags, sizeof(uint64_t), pfn * sizeof(uint64_t)) != 85 sizeof(uint64_t)) { 86 PLOG(ERROR) << "Failed to read page flags for page " << pfn; 87 return false; 88 } 89 return true; 90 } 91 92 bool PageAcct::PageMapCount(uint64_t pfn, uint64_t* mapcount) { 93 if (!mapcount) return false; 94 95 if (kpagecount_fd_ < 0) { 96 if (!InitPageAcct()) return false; 97 } 98 99 if (pread64(kpagecount_fd_, mapcount, sizeof(uint64_t), pfn * sizeof(uint64_t)) != 100 sizeof(uint64_t)) { 101 PLOG(ERROR) << "Failed to read map count for page " << pfn; 102 return false; 103 } 104 return true; 105 } 106 107 int PageAcct::IsPageIdle(uint64_t pfn) { 108 if (pageidle_fd_ < 0) { 109 if (!InitPageAcct(true)) return -EOPNOTSUPP; 110 } 111 112 int idle_status = MarkPageIdle(pfn); 113 if (idle_status) return idle_status; 114 115 return GetPageIdle(pfn); 116 } 117 118 int PageAcct::MarkPageIdle(uint64_t pfn) const { 119 off64_t offset = pfn_to_idle_bitmap_offset(pfn); 120 // set the bit corresponding to page frame 121 uint64_t idle_bits = 1ULL << (pfn % 64); 122 123 if (pwrite64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) { 124 PLOG(ERROR) << "Failed to write page idle bitmap for page " << pfn; 125 return -errno; 126 } 127 128 return 0; 129 } 130 131 int PageAcct::GetPageIdle(uint64_t pfn) const { 132 off64_t offset = pfn_to_idle_bitmap_offset(pfn); 133 uint64_t idle_bits; 134 135 if (pread64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) != sizeof(uint64_t)) { 136 PLOG(ERROR) << "Failed to read page idle bitmap for page " << pfn; 137 return -errno; 138 } 139 140 return !!(idle_bits & (1ULL << (pfn % 64))); 141 } 142 143 // Public methods 144 bool page_present(uint64_t pagemap_val) { 145 return PAGE_PRESENT(pagemap_val); 146 } 147 148 bool page_swapped(uint64_t pagemap_val) { 149 return PAGE_SWAPPED(pagemap_val); 150 } 151 152 uint64_t page_pfn(uint64_t pagemap_val) { 153 return PAGE_PFN(pagemap_val); 154 } 155 156 } // namespace meminfo 157 } // namespace android 158