| .. | .. |
|---|
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
|---|
| 2 | | -#include <linux/bootmem.h> |
|---|
| 2 | +#include <linux/memblock.h> |
|---|
| 3 | 3 | #include <linux/compiler.h> |
|---|
| 4 | 4 | #include <linux/fs.h> |
|---|
| 5 | 5 | #include <linux/init.h> |
|---|
| .. | .. |
|---|
| 21 | 21 | #define KPMMASK (KPMSIZE - 1) |
|---|
| 22 | 22 | #define KPMBITS (KPMSIZE * BITS_PER_BYTE) |
|---|
| 23 | 23 | |
|---|
| 24 | +static inline unsigned long get_max_dump_pfn(void) |
|---|
| 25 | +{ |
|---|
| 26 | +#ifdef CONFIG_SPARSEMEM |
|---|
| 27 | + /* |
|---|
| 28 | + * The memmap of early sections is completely populated and marked |
|---|
| 29 | + * online even if max_pfn does not fall on a section boundary - |
|---|
| 30 | + * pfn_to_online_page() will succeed on all pages. Allow inspecting |
|---|
| 31 | + * these memmaps. |
|---|
| 32 | + */ |
|---|
| 33 | + return round_up(max_pfn, PAGES_PER_SECTION); |
|---|
| 34 | +#else |
|---|
| 35 | + return max_pfn; |
|---|
| 36 | +#endif |
|---|
| 37 | +} |
|---|
| 38 | + |
|---|
| 24 | 39 | /* /proc/kpagecount - an array exposing page counts |
|---|
| 25 | 40 | * |
|---|
| 26 | 41 | * Each entry is a u64 representing the corresponding |
|---|
| .. | .. |
|---|
| 29 | 44 | static ssize_t kpagecount_read(struct file *file, char __user *buf, |
|---|
| 30 | 45 | size_t count, loff_t *ppos) |
|---|
| 31 | 46 | { |
|---|
| 47 | + const unsigned long max_dump_pfn = get_max_dump_pfn(); |
|---|
| 32 | 48 | u64 __user *out = (u64 __user *)buf; |
|---|
| 33 | 49 | struct page *ppage; |
|---|
| 34 | 50 | unsigned long src = *ppos; |
|---|
| .. | .. |
|---|
| 37 | 53 | u64 pcount; |
|---|
| 38 | 54 | |
|---|
| 39 | 55 | pfn = src / KPMSIZE; |
|---|
| 40 | | - count = min_t(size_t, count, (max_pfn * KPMSIZE) - src); |
|---|
| 41 | 56 | if (src & KPMMASK || count & KPMMASK) |
|---|
| 42 | 57 | return -EINVAL; |
|---|
| 58 | + if (src >= max_dump_pfn * KPMSIZE) |
|---|
| 59 | + return 0; |
|---|
| 60 | + count = min_t(unsigned long, count, (max_dump_pfn * KPMSIZE) - src); |
|---|
| 43 | 61 | |
|---|
| 44 | 62 | while (count > 0) { |
|---|
| 45 | 63 | /* |
|---|
| .. | .. |
|---|
| 48 | 66 | */ |
|---|
| 49 | 67 | ppage = pfn_to_online_page(pfn); |
|---|
| 50 | 68 | |
|---|
| 51 | | - if (!ppage || PageSlab(ppage)) |
|---|
| 69 | + if (!ppage || PageSlab(ppage) || page_has_type(ppage)) |
|---|
| 52 | 70 | pcount = 0; |
|---|
| 53 | 71 | else |
|---|
| 54 | 72 | pcount = page_mapcount(ppage); |
|---|
| .. | .. |
|---|
| 71 | 89 | return ret; |
|---|
| 72 | 90 | } |
|---|
| 73 | 91 | |
|---|
| 74 | | -static const struct file_operations proc_kpagecount_operations = { |
|---|
| 75 | | - .llseek = mem_lseek, |
|---|
| 76 | | - .read = kpagecount_read, |
|---|
| 92 | +static const struct proc_ops kpagecount_proc_ops = { |
|---|
| 93 | + .proc_lseek = mem_lseek, |
|---|
| 94 | + .proc_read = kpagecount_read, |
|---|
| 77 | 95 | }; |
|---|
| 78 | 96 | |
|---|
| 79 | 97 | /* /proc/kpageflags - an array exposing page flags |
|---|
| .. | .. |
|---|
| 154 | 172 | else if (page_count(page) == 0 && is_free_buddy_page(page)) |
|---|
| 155 | 173 | u |= 1 << KPF_BUDDY; |
|---|
| 156 | 174 | |
|---|
| 157 | | - if (PageBalloon(page)) |
|---|
| 158 | | - u |= 1 << KPF_BALLOON; |
|---|
| 175 | + if (PageOffline(page)) |
|---|
| 176 | + u |= 1 << KPF_OFFLINE; |
|---|
| 159 | 177 | if (PageTable(page)) |
|---|
| 160 | 178 | u |= 1 << KPF_PGTABLE; |
|---|
| 161 | 179 | |
|---|
| .. | .. |
|---|
| 199 | 217 | u |= kpf_copy_bit(k, KPF_PRIVATE_2, PG_private_2); |
|---|
| 200 | 218 | u |= kpf_copy_bit(k, KPF_OWNER_PRIVATE, PG_owner_priv_1); |
|---|
| 201 | 219 | u |= kpf_copy_bit(k, KPF_ARCH, PG_arch_1); |
|---|
| 220 | +#ifdef CONFIG_64BIT |
|---|
| 221 | + u |= kpf_copy_bit(k, KPF_ARCH_2, PG_arch_2); |
|---|
| 222 | +#endif |
|---|
| 202 | 223 | |
|---|
| 203 | 224 | return u; |
|---|
| 204 | 225 | }; |
|---|
| .. | .. |
|---|
| 206 | 227 | static ssize_t kpageflags_read(struct file *file, char __user *buf, |
|---|
| 207 | 228 | size_t count, loff_t *ppos) |
|---|
| 208 | 229 | { |
|---|
| 230 | + const unsigned long max_dump_pfn = get_max_dump_pfn(); |
|---|
| 209 | 231 | u64 __user *out = (u64 __user *)buf; |
|---|
| 210 | 232 | struct page *ppage; |
|---|
| 211 | 233 | unsigned long src = *ppos; |
|---|
| .. | .. |
|---|
| 213 | 235 | ssize_t ret = 0; |
|---|
| 214 | 236 | |
|---|
| 215 | 237 | pfn = src / KPMSIZE; |
|---|
| 216 | | - count = min_t(unsigned long, count, (max_pfn * KPMSIZE) - src); |
|---|
| 217 | 238 | if (src & KPMMASK || count & KPMMASK) |
|---|
| 218 | 239 | return -EINVAL; |
|---|
| 240 | + if (src >= max_dump_pfn * KPMSIZE) |
|---|
| 241 | + return 0; |
|---|
| 242 | + count = min_t(unsigned long, count, (max_dump_pfn * KPMSIZE) - src); |
|---|
| 219 | 243 | |
|---|
| 220 | 244 | while (count > 0) { |
|---|
| 221 | 245 | /* |
|---|
| .. | .. |
|---|
| 242 | 266 | return ret; |
|---|
| 243 | 267 | } |
|---|
| 244 | 268 | |
|---|
| 245 | | -static const struct file_operations proc_kpageflags_operations = { |
|---|
| 246 | | - .llseek = mem_lseek, |
|---|
| 247 | | - .read = kpageflags_read, |
|---|
| 269 | +static const struct proc_ops kpageflags_proc_ops = { |
|---|
| 270 | + .proc_lseek = mem_lseek, |
|---|
| 271 | + .proc_read = kpageflags_read, |
|---|
| 248 | 272 | }; |
|---|
| 249 | 273 | |
|---|
| 250 | 274 | #ifdef CONFIG_MEMCG |
|---|
| 251 | 275 | static ssize_t kpagecgroup_read(struct file *file, char __user *buf, |
|---|
| 252 | 276 | size_t count, loff_t *ppos) |
|---|
| 253 | 277 | { |
|---|
| 278 | + const unsigned long max_dump_pfn = get_max_dump_pfn(); |
|---|
| 254 | 279 | u64 __user *out = (u64 __user *)buf; |
|---|
| 255 | 280 | struct page *ppage; |
|---|
| 256 | 281 | unsigned long src = *ppos; |
|---|
| .. | .. |
|---|
| 259 | 284 | u64 ino; |
|---|
| 260 | 285 | |
|---|
| 261 | 286 | pfn = src / KPMSIZE; |
|---|
| 262 | | - count = min_t(unsigned long, count, (max_pfn * KPMSIZE) - src); |
|---|
| 263 | 287 | if (src & KPMMASK || count & KPMMASK) |
|---|
| 264 | 288 | return -EINVAL; |
|---|
| 289 | + if (src >= max_dump_pfn * KPMSIZE) |
|---|
| 290 | + return 0; |
|---|
| 291 | + count = min_t(unsigned long, count, (max_dump_pfn * KPMSIZE) - src); |
|---|
| 265 | 292 | |
|---|
| 266 | 293 | while (count > 0) { |
|---|
| 267 | 294 | /* |
|---|
| .. | .. |
|---|
| 293 | 320 | return ret; |
|---|
| 294 | 321 | } |
|---|
| 295 | 322 | |
|---|
| 296 | | -static const struct file_operations proc_kpagecgroup_operations = { |
|---|
| 297 | | - .llseek = mem_lseek, |
|---|
| 298 | | - .read = kpagecgroup_read, |
|---|
| 323 | +static const struct proc_ops kpagecgroup_proc_ops = { |
|---|
| 324 | + .proc_lseek = mem_lseek, |
|---|
| 325 | + .proc_read = kpagecgroup_read, |
|---|
| 299 | 326 | }; |
|---|
| 300 | 327 | #endif /* CONFIG_MEMCG */ |
|---|
| 301 | 328 | |
|---|
| 302 | 329 | static int __init proc_page_init(void) |
|---|
| 303 | 330 | { |
|---|
| 304 | | - proc_create("kpagecount", S_IRUSR, NULL, &proc_kpagecount_operations); |
|---|
| 305 | | - proc_create("kpageflags", S_IRUSR, NULL, &proc_kpageflags_operations); |
|---|
| 331 | + proc_create("kpagecount", S_IRUSR, NULL, &kpagecount_proc_ops); |
|---|
| 332 | + proc_create("kpageflags", S_IRUSR, NULL, &kpageflags_proc_ops); |
|---|
| 306 | 333 | #ifdef CONFIG_MEMCG |
|---|
| 307 | | - proc_create("kpagecgroup", S_IRUSR, NULL, &proc_kpagecgroup_operations); |
|---|
| 334 | + proc_create("kpagecgroup", S_IRUSR, NULL, &kpagecgroup_proc_ops); |
|---|
| 308 | 335 | #endif |
|---|
| 309 | 336 | return 0; |
|---|
| 310 | 337 | } |
|---|