-
Notifications
You must be signed in to change notification settings - Fork 0
/
function.c
182 lines (158 loc) · 5.47 KB
/
function.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
int __ref reclaim_mram_pages(unsigned long start_pfn, unsigned long nr_pages,
struct memory_group *group, struct dev_pagemap *pgmap)
{
const unsigned long end_pfn = start_pfn + nr_pages;
unsigned long pfn, pfn_it, system_ram_pages = 0;
unsigned long flags;
struct zone *zone;
struct memory_notify arg;
int ret, node, i;
char *reason;
struct mhp_params params = {
.altmap = pgmap_altmap(pgmap),
.pgprot = PAGE_KERNEL,
};
struct range *range;
if (WARN_ON_ONCE(!nr_pages ||
!IS_ALIGNED(start_pfn, pageblock_nr_pages) ||
!IS_ALIGNED(start_pfn + nr_pages, PAGES_PER_SECTION)))
return -EINVAL;
mem_hotplug_begin();
zone = test_pages_in_a_zone(start_pfn, end_pfn);
if (!zone)
{
ret = -EINVAL;
reason = "multizone range";
goto failed_removal;
}
node = zone_to_nid(zone);
zone_pcp_disable(zone);
lru_cache_disable();
ret = start_isolate_page_range(start_pfn, end_pfn,
MIGRATE_MOVABLE,
MEMORY_OFFLINE | REPORT_FAILURE);
if (ret)
{
reason = "failure to isolate range";
goto failed_removal_pcplists_disabled;
}
arg.start_pfn = start_pfn;
arg.nr_pages = nr_pages;
node_states_check_changes_offline(nr_pages, zone, &arg);
ret = memory_notify(MEM_GOING_OFFLINE, &arg);
ret = notifier_to_errno(ret);
if (ret)
{
reason = "notifier failure";
goto failed_removal_isolated;
}
for (pfn_it = start_pfn; pfn_it < end_pfn; pfn_it += 64)
{
do
{
/* We scan and perform migrations only for MRAM pages */
pfn = pfn_it;
do
{
if (signal_pending(current))
{
ret = -EINTR;
reason = "signal backoff";
goto failed_removal_isolated;
}
cond_resched();
ret = scan_movable_pages(pfn, pfn_it + 32, &pfn);
if (!ret)
{
/*
* TODO: fatal migration failures should bail
* out
*/
do_migrate_range(pfn, pfn_it + 32);
}
} while (!ret);
if (ret != -ENOENT)
{
reason = "unmovable page";
goto failed_removal_isolated;
}
/*
* Dissolve free hugepages in the memory block before doing
* offlining actually in order to make hugetlbfs's object
* counting consistent.
*/
ret = dissolve_free_huge_pages(pfn_it, pfn_it + 32);
if (ret)
{
reason = "failure to dissolve huge pages";
goto failed_removal_isolated;
}
ret = test_pages_isolated(pfn_it, pfn_it + 32, MEMORY_OFFLINE);
} while (ret);
}
/* Mark all sections offline and remove free pages from the buddy. */
for (pfn_it = start_pfn; pfn_it < end_pfn; pfn_it += 64)
__offline_isolated_pages(pfn_it, pfn_it + 32);
pr_debug("Offlined Pages %ld\n", nr_pages);
/*
* The memory sections are marked offline, and the pageblock flags
* effectively stale; nobody should be touching them. Fixup the number
* of isolated pageblocks, memory onlining will properly revert this.
*/
spin_lock_irqsave(&zone->lock, flags);
zone->nr_isolate_pageblock -= nr_pages / pageblock_nr_pages;
spin_unlock_irqrestore(&zone->lock, flags);
lru_cache_enable();
zone_pcp_enable(zone);
/* removal success */
/* Only half of the pages in the range is managed by the buddy */
adjust_managed_page_count(pfn_to_page(start_pfn), -(nr_pages >> 1));
adjust_present_page_count(pfn_to_page(start_pfn), group, -nr_pages);
/* reinitialise watermarks and update pcp limits */
init_per_zone_wmark_min();
if (!populated_zone(zone))
{
zone_pcp_reset(zone);
build_all_zonelists(NULL);
}
node_states_clear_node(node, &arg);
if (arg.status_change_nid >= 0)
{
kswapd_stop(node);
kcompactd_stop(node);
}
writeback_set_ratelimit();
memory_notify(MEM_OFFLINE, &arg);
remove_pfn_range_from_zone(zone, start_pfn, nr_pages);
for (i = 0; i < pgmap->nr_range; i++)
{
range = &pgmap->ranges[i];
move_pfn_range_to_zone(zone, start_pfn,
nr_pages, params.altmap,
MIGRATE_MOVABLE);
}
mem_hotplug_done();
for (i = 0; i < pgmap->nr_range; i++)
{
range = &pgmap->ranges[i];
memmap_init_zone_device(&NODE_DATA(node)->node_zones[ZONE_DEVICE],
start_pfn,
nr_pages, pgmap);
}
return 0;
failed_removal_isolated:
undo_isolate_page_range(start_pfn, end_pfn, MIGRATE_MOVABLE);
memory_notify(MEM_CANCEL_OFFLINE, &arg);
failed_removal_pcplists_disabled:
lru_cache_enable();
zone_pcp_enable(zone);
failed_removal:
pr_info("MRAM offlining [mem %#010llx-%#010llx] failed due to %s\n",
(unsigned long long)start_pfn << PAGE_SHIFT,
((unsigned long long)end_pfn << PAGE_SHIFT) - 1,
reason);
/* pushback to free area */
mem_hotplug_done();
return ret;
}
EXPORT_SYMBOL(reclaim_mram_pages);