2 * Copyright (C) IBM Corporation, 2014, 2017
3 * Anton Blanchard, Rashmica Gupta.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
11 #define pr_fmt(fmt) "memtrace: " fmt
13 #include <linux/bitops.h>
14 #include <linux/string.h>
15 #include <linux/memblock.h>
16 #include <linux/init.h>
17 #include <linux/moduleparam.h>
19 #include <linux/debugfs.h>
20 #include <linux/slab.h>
21 #include <linux/memory.h>
22 #include <linux/memory_hotplug.h>
23 #include <asm/machdep.h>
24 #include <asm/debugfs.h>
26 /* This enables us to keep track of the memory removed from each node. */
27 struct memtrace_entry
{
36 static u64 memtrace_size
;
38 static struct memtrace_entry
*memtrace_array
;
39 static unsigned int memtrace_array_nr
;
42 static ssize_t
memtrace_read(struct file
*filp
, char __user
*ubuf
,
43 size_t count
, loff_t
*ppos
)
45 struct memtrace_entry
*ent
= filp
->private_data
;
47 return simple_read_from_buffer(ubuf
, count
, ppos
, ent
->mem
, ent
->size
);
50 static bool valid_memtrace_range(struct memtrace_entry
*dev
,
51 unsigned long start
, unsigned long size
)
53 if ((start
>= dev
->start
) &&
54 ((start
+ size
) <= (dev
->start
+ dev
->size
)))
60 static int memtrace_mmap(struct file
*filp
, struct vm_area_struct
*vma
)
62 unsigned long size
= vma
->vm_end
- vma
->vm_start
;
63 struct memtrace_entry
*dev
= filp
->private_data
;
65 if (!valid_memtrace_range(dev
, vma
->vm_pgoff
<< PAGE_SHIFT
, size
))
68 vma
->vm_page_prot
= pgprot_noncached(vma
->vm_page_prot
);
70 if (remap_pfn_range(vma
, vma
->vm_start
,
71 vma
->vm_pgoff
+ (dev
->start
>> PAGE_SHIFT
),
72 size
, vma
->vm_page_prot
))
78 static const struct file_operations memtrace_fops
= {
79 .llseek
= default_llseek
,
80 .read
= memtrace_read
,
81 .mmap
= memtrace_mmap
,
85 static int check_memblock_online(struct memory_block
*mem
, void *arg
)
87 if (mem
->state
!= MEM_ONLINE
)
93 static int change_memblock_state(struct memory_block
*mem
, void *arg
)
95 unsigned long state
= (unsigned long)arg
;
102 static bool memtrace_offline_pages(u32 nid
, u64 start_pfn
, u64 nr_pages
)
104 u64 end_pfn
= start_pfn
+ nr_pages
- 1;
106 if (walk_memory_range(start_pfn
, end_pfn
, NULL
,
107 check_memblock_online
))
110 walk_memory_range(start_pfn
, end_pfn
, (void *)MEM_GOING_OFFLINE
,
111 change_memblock_state
);
113 if (offline_pages(start_pfn
, nr_pages
)) {
114 walk_memory_range(start_pfn
, end_pfn
, (void *)MEM_ONLINE
,
115 change_memblock_state
);
119 walk_memory_range(start_pfn
, end_pfn
, (void *)MEM_OFFLINE
,
120 change_memblock_state
);
122 lock_device_hotplug();
123 remove_memory(nid
, start_pfn
<< PAGE_SHIFT
, nr_pages
<< PAGE_SHIFT
);
124 unlock_device_hotplug();
129 static u64
memtrace_alloc_node(u32 nid
, u64 size
)
131 u64 start_pfn
, end_pfn
, nr_pages
;
134 if (!NODE_DATA(nid
) || !node_spanned_pages(nid
))
137 start_pfn
= node_start_pfn(nid
);
138 end_pfn
= node_end_pfn(nid
);
139 nr_pages
= size
>> PAGE_SHIFT
;
141 /* Trace memory needs to be aligned to the size */
142 end_pfn
= round_down(end_pfn
- nr_pages
, nr_pages
);
144 for (base_pfn
= end_pfn
; base_pfn
> start_pfn
; base_pfn
-= nr_pages
) {
145 if (memtrace_offline_pages(nid
, base_pfn
, nr_pages
) == true)
146 return base_pfn
<< PAGE_SHIFT
;
152 static int memtrace_init_regions_runtime(u64 size
)
157 memtrace_array
= kcalloc(num_online_nodes(),
158 sizeof(struct memtrace_entry
), GFP_KERNEL
);
159 if (!memtrace_array
) {
160 pr_err("Failed to allocate memtrace_array\n");
164 for_each_online_node(nid
) {
165 m
= memtrace_alloc_node(nid
, size
);
168 * A node might not have any local memory, so warn but
172 pr_err("Failed to allocate trace memory on node %d\n", nid
);
176 pr_info("Allocated trace memory on node %d at 0x%016llx\n", nid
, m
);
178 memtrace_array
[memtrace_array_nr
].start
= m
;
179 memtrace_array
[memtrace_array_nr
].size
= size
;
180 memtrace_array
[memtrace_array_nr
].nid
= nid
;
187 static struct dentry
*memtrace_debugfs_dir
;
189 static int memtrace_init_debugfs(void)
194 for (i
= 0; i
< memtrace_array_nr
; i
++) {
196 struct memtrace_entry
*ent
= &memtrace_array
[i
];
198 ent
->mem
= ioremap(ent
->start
, ent
->size
);
199 /* Warn but continue on */
201 pr_err("Failed to map trace memory at 0x%llx\n",
207 snprintf(ent
->name
, 16, "%08x", ent
->nid
);
208 dir
= debugfs_create_dir(ent
->name
, memtrace_debugfs_dir
);
213 debugfs_create_file("trace", 0400, dir
, ent
, &memtrace_fops
);
214 debugfs_create_x64("start", 0400, dir
, &ent
->start
);
215 debugfs_create_x64("size", 0400, dir
, &ent
->size
);
221 static int memtrace_enable_set(void *data
, u64 val
)
229 /* Make sure size is aligned to a memory block */
230 if (val
& (memory_block_size_bytes() - 1))
233 if (memtrace_init_regions_runtime(val
))
236 if (memtrace_init_debugfs())
244 static int memtrace_enable_get(void *data
, u64
*val
)
246 *val
= memtrace_size
;
250 DEFINE_SIMPLE_ATTRIBUTE(memtrace_init_fops
, memtrace_enable_get
,
251 memtrace_enable_set
, "0x%016llx\n");
253 static int memtrace_init(void)
255 memtrace_debugfs_dir
= debugfs_create_dir("memtrace",
256 powerpc_debugfs_root
);
257 if (!memtrace_debugfs_dir
)
260 debugfs_create_file("enable", 0600, memtrace_debugfs_dir
,
261 NULL
, &memtrace_init_fops
);
265 machine_device_initcall(powernv
, memtrace_init
);