]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | Subject: [PATCH] ehea: Fix memory hotplug support |
2 | Subject [PATCH] ehea: Add hugepage detection | |
3 | From: Hannes Hering <hannes.hering@linux.vnet.ibm.com> | |
4 | References: 436447 - LTC 48713 | |
5 | References: 439599 - LTC 48958 | |
6 | References: 477972 - LTC 51731 | |
7 | ||
8 | This patch implements the memory notifier to update the busmap instantly | |
9 | instead of rebuilding the whole map. This is necessary because | |
10 | walk_memory_resource provides different information than required during memory | |
11 | hotplug. | |
12 | ||
13 | ... | |
14 | ||
15 | All kernel memory which is used for kernel/hardware data transfer must be registered | |
16 | with firmware using "memory regions". 16GB hugepages may not be part of a memory region | |
17 | due to firmware restrictions. | |
18 | This patch modifies the walk_memory_resource callback fn to filter hugepages and add | |
19 | only standard memory to the busmap which is later on used for MR registration. | |
20 | ||
21 | ... | |
22 | ||
23 | Added missing set_bit() to disable data transfer when a memchange notification is handled | |
24 | ||
25 | ... | |
26 | ||
27 | Signed-off-by: Thomas Klein <tklein@de.ibm.com> | |
28 | Signed-off-by: Hannes Hering <hering2@de.ibm.com> | |
29 | Signed-off-by: Olaf Hering <olh@suse.de> | |
30 | --- | |
31 | drivers/net/ehea/ehea.h | 4 | |
32 | drivers/net/ehea/ehea_main.c | 27 ++++-- | |
33 | drivers/net/ehea/ehea_phyp.c | 2 | |
34 | drivers/net/ehea/ehea_qmr.c | 175 ++++++++++++++++++++++++++++++++++++------- | |
35 | drivers/net/ehea/ehea_qmr.h | 5 + | |
36 | 5 files changed, 172 insertions(+), 41 deletions(-) | |
37 | ||
38 | --- a/drivers/net/ehea/ehea.h | |
39 | +++ b/drivers/net/ehea/ehea.h | |
40 | @@ -40,13 +40,13 @@ | |
41 | #include <asm/io.h> | |
42 | ||
43 | #define DRV_NAME "ehea" | |
44 | -#define DRV_VERSION "EHEA_0092" | |
45 | +#define DRV_VERSION "EHEA_0094-02" | |
46 | ||
47 | /* eHEA capability flags */ | |
48 | #define DLPAR_PORT_ADD_REM 1 | |
49 | #define DLPAR_MEM_ADD 2 | |
50 | #define DLPAR_MEM_REM 4 | |
51 | -#define EHEA_CAPABILITIES (DLPAR_PORT_ADD_REM | DLPAR_MEM_ADD) | |
52 | +#define EHEA_CAPABILITIES (DLPAR_PORT_ADD_REM | DLPAR_MEM_ADD | DLPAR_MEM_REM) | |
53 | ||
54 | #define EHEA_MSG_DEFAULT (NETIF_MSG_LINK | NETIF_MSG_TIMER \ | |
55 | | NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR) | |
56 | --- a/drivers/net/ehea/ehea_main.c | |
57 | +++ b/drivers/net/ehea/ehea_main.c | |
58 | @@ -2869,7 +2869,7 @@ static void ehea_rereg_mrs(struct work_s | |
59 | struct ehea_adapter *adapter; | |
60 | ||
61 | mutex_lock(&dlpar_mem_lock); | |
62 | - ehea_info("LPAR memory enlarged - re-initializing driver"); | |
63 | + ehea_info("LPAR memory changed - re-initializing driver"); | |
64 | ||
65 | list_for_each_entry(adapter, &adapter_list, list) | |
66 | if (adapter->active_ports) { | |
67 | @@ -2906,13 +2906,6 @@ static void ehea_rereg_mrs(struct work_s | |
68 | } | |
69 | } | |
70 | ||
71 | - ehea_destroy_busmap(); | |
72 | - ret = ehea_create_busmap(); | |
73 | - if (ret) { | |
74 | - ehea_error("creating ehea busmap failed"); | |
75 | - goto out; | |
76 | - } | |
77 | - | |
78 | clear_bit(__EHEA_STOP_XFER, &ehea_driver_flags); | |
79 | ||
80 | list_for_each_entry(adapter, &adapter_list, list) | |
81 | @@ -3525,9 +3518,23 @@ void ehea_crash_handler(void) | |
82 | static int ehea_mem_notifier(struct notifier_block *nb, | |
83 | unsigned long action, void *data) | |
84 | { | |
85 | + struct memory_notify *arg = data; | |
86 | switch (action) { | |
87 | - case MEM_OFFLINE: | |
88 | - ehea_info("memory has been removed"); | |
89 | + case MEM_CANCEL_OFFLINE: | |
90 | + ehea_info("memory offlining canceled"); | |
91 | + /* Readd canceled memory block */ | |
92 | + case MEM_ONLINE: | |
93 | + ehea_info("memory is going online"); | |
94 | + set_bit(__EHEA_STOP_XFER, &ehea_driver_flags); | |
95 | + if (ehea_add_sect_bmap(arg->start_pfn, arg->nr_pages)) | |
96 | + return NOTIFY_BAD; | |
97 | + ehea_rereg_mrs(NULL); | |
98 | + break; | |
99 | + case MEM_GOING_OFFLINE: | |
100 | + ehea_info("memory is going offline"); | |
101 | + set_bit(__EHEA_STOP_XFER, &ehea_driver_flags); | |
102 | + if (ehea_rem_sect_bmap(arg->start_pfn, arg->nr_pages)) | |
103 | + return NOTIFY_BAD; | |
104 | ehea_rereg_mrs(NULL); | |
105 | break; | |
106 | default: | |
107 | --- a/drivers/net/ehea/ehea_phyp.c | |
108 | +++ b/drivers/net/ehea/ehea_phyp.c | |
109 | @@ -535,7 +535,7 @@ u64 ehea_h_query_ehea(const u64 adapter_ | |
110 | cb_logaddr, /* R5 */ | |
111 | 0, 0, 0, 0, 0); /* R6-R10 */ | |
112 | #ifdef DEBUG | |
113 | - ehea_dmp(cb_addr, sizeof(struct hcp_query_ehea), "hcp_query_ehea"); | |
114 | + ehea_dump(cb_addr, sizeof(struct hcp_query_ehea), "hcp_query_ehea"); | |
115 | #endif | |
116 | return hret; | |
117 | } | |
118 | --- a/drivers/net/ehea/ehea_qmr.c | |
119 | +++ b/drivers/net/ehea/ehea_qmr.c | |
120 | @@ -567,7 +567,7 @@ static inline int ehea_calc_index(unsign | |
121 | static inline int ehea_init_top_bmap(struct ehea_top_bmap *ehea_top_bmap, | |
122 | int dir) | |
123 | { | |
124 | - if(!ehea_top_bmap->dir[dir]) { | |
125 | + if (!ehea_top_bmap->dir[dir]) { | |
126 | ehea_top_bmap->dir[dir] = | |
127 | kzalloc(sizeof(struct ehea_dir_bmap), GFP_KERNEL); | |
128 | if (!ehea_top_bmap->dir[dir]) | |
129 | @@ -578,7 +578,7 @@ static inline int ehea_init_top_bmap(str | |
130 | ||
131 | static inline int ehea_init_bmap(struct ehea_bmap *ehea_bmap, int top, int dir) | |
132 | { | |
133 | - if(!ehea_bmap->top[top]) { | |
134 | + if (!ehea_bmap->top[top]) { | |
135 | ehea_bmap->top[top] = | |
136 | kzalloc(sizeof(struct ehea_top_bmap), GFP_KERNEL); | |
137 | if (!ehea_bmap->top[top]) | |
138 | @@ -587,52 +587,171 @@ static inline int ehea_init_bmap(struct | |
139 | return ehea_init_top_bmap(ehea_bmap->top[top], dir); | |
140 | } | |
141 | ||
142 | -static int ehea_create_busmap_callback(unsigned long pfn, | |
143 | - unsigned long nr_pages, void *arg) | |
144 | +static DEFINE_MUTEX(ehea_busmap_mutex); | |
145 | +static unsigned long ehea_mr_len; | |
146 | + | |
147 | +#define EHEA_BUSMAP_ADD_SECT 1 | |
148 | +#define EHEA_BUSMAP_REM_SECT 0 | |
149 | + | |
150 | +static void ehea_rebuild_busmap(void) | |
151 | +{ | |
152 | + u64 vaddr = EHEA_BUSMAP_START; | |
153 | + int top, dir, idx; | |
154 | + | |
155 | + for (top = 0; top < EHEA_MAP_ENTRIES; top++) { | |
156 | + struct ehea_top_bmap *ehea_top; | |
157 | + int valid_dir_entries = 0; | |
158 | + | |
159 | + if (!ehea_bmap->top[top]) | |
160 | + continue; | |
161 | + ehea_top = ehea_bmap->top[top]; | |
162 | + for (dir = 0; dir < EHEA_MAP_ENTRIES; dir++) { | |
163 | + struct ehea_dir_bmap *ehea_dir; | |
164 | + int valid_entries = 0; | |
165 | + | |
166 | + if (!ehea_top->dir[dir]) | |
167 | + continue; | |
168 | + valid_dir_entries++; | |
169 | + ehea_dir = ehea_top->dir[dir]; | |
170 | + for (idx = 0; idx < EHEA_MAP_ENTRIES; idx++) { | |
171 | + if (!ehea_dir->ent[idx]) | |
172 | + continue; | |
173 | + valid_entries++; | |
174 | + ehea_dir->ent[idx] = vaddr; | |
175 | + vaddr += EHEA_SECTSIZE; | |
176 | + } | |
177 | + if (!valid_entries) { | |
178 | + ehea_top->dir[dir] = NULL; | |
179 | + kfree(ehea_dir); | |
180 | + } | |
181 | + } | |
182 | + if (!valid_dir_entries) { | |
183 | + ehea_bmap->top[top] = NULL; | |
184 | + kfree(ehea_top); | |
185 | + } | |
186 | + } | |
187 | +} | |
188 | + | |
189 | +static int ehea_update_busmap(unsigned long pfn, unsigned long nr_pages, int add) | |
190 | { | |
191 | - unsigned long i, mr_len, start_section, end_section; | |
192 | + unsigned long i, start_section, end_section; | |
193 | + | |
194 | + if (!nr_pages) | |
195 | + return 0; | |
196 | + | |
197 | + if (!ehea_bmap) { | |
198 | + ehea_bmap = kzalloc(sizeof(struct ehea_bmap), GFP_KERNEL); | |
199 | + if (!ehea_bmap) | |
200 | + return -ENOMEM; | |
201 | + } | |
202 | + | |
203 | start_section = (pfn * PAGE_SIZE) / EHEA_SECTSIZE; | |
204 | end_section = start_section + ((nr_pages * PAGE_SIZE) / EHEA_SECTSIZE); | |
205 | - mr_len = *(unsigned long *)arg; | |
206 | + /* Mark entries as valid or invalid only; address is assigned later */ | |
207 | + for (i = start_section; i < end_section; i++) { | |
208 | + u64 flag; | |
209 | + int top = ehea_calc_index(i, EHEA_TOP_INDEX_SHIFT); | |
210 | + int dir = ehea_calc_index(i, EHEA_DIR_INDEX_SHIFT); | |
211 | + int idx = i & EHEA_INDEX_MASK; | |
212 | + | |
213 | + if (add) { | |
214 | + int ret = ehea_init_bmap(ehea_bmap, top, dir); | |
215 | + if (ret) | |
216 | + return ret; | |
217 | + flag = 1; /* valid */ | |
218 | + ehea_mr_len += EHEA_SECTSIZE; | |
219 | + } else { | |
220 | + if (!ehea_bmap->top[top]) | |
221 | + continue; | |
222 | + if (!ehea_bmap->top[top]->dir[dir]) | |
223 | + continue; | |
224 | + flag = 0; /* invalid */ | |
225 | + ehea_mr_len -= EHEA_SECTSIZE; | |
226 | + } | |
227 | ||
228 | - ehea_bmap = kzalloc(sizeof(struct ehea_bmap), GFP_KERNEL); | |
229 | - if (!ehea_bmap) | |
230 | - return -ENOMEM; | |
231 | + ehea_bmap->top[top]->dir[dir]->ent[idx] = flag; | |
232 | + } | |
233 | + ehea_rebuild_busmap(); /* Assign contiguous addresses for mr */ | |
234 | + return 0; | |
235 | +} | |
236 | ||
237 | - for (i = start_section; i < end_section; i++) { | |
238 | - int ret; | |
239 | - int top, dir, idx; | |
240 | - u64 vaddr; | |
241 | +int ehea_add_sect_bmap(unsigned long pfn, unsigned long nr_pages) | |
242 | +{ | |
243 | + int ret; | |
244 | ||
245 | - top = ehea_calc_index(i, EHEA_TOP_INDEX_SHIFT); | |
246 | - dir = ehea_calc_index(i, EHEA_DIR_INDEX_SHIFT); | |
247 | + mutex_lock(&ehea_busmap_mutex); | |
248 | + ret = ehea_update_busmap(pfn, nr_pages, EHEA_BUSMAP_ADD_SECT); | |
249 | + mutex_unlock(&ehea_busmap_mutex); | |
250 | + return ret; | |
251 | +} | |
252 | ||
253 | - ret = ehea_init_bmap(ehea_bmap, top, dir); | |
254 | - if(ret) | |
255 | - return ret; | |
256 | +int ehea_rem_sect_bmap(unsigned long pfn, unsigned long nr_pages) | |
257 | +{ | |
258 | + int ret; | |
259 | ||
260 | - idx = i & EHEA_INDEX_MASK; | |
261 | - vaddr = EHEA_BUSMAP_START + mr_len + i * EHEA_SECTSIZE; | |
262 | + mutex_lock(&ehea_busmap_mutex); | |
263 | + ret = ehea_update_busmap(pfn, nr_pages, EHEA_BUSMAP_REM_SECT); | |
264 | + mutex_unlock(&ehea_busmap_mutex); | |
265 | + return ret; | |
266 | +} | |
267 | ||
268 | - ehea_bmap->top[top]->dir[dir]->ent[idx] = vaddr; | |
269 | - } | |
270 | +static int ehea_is_hugepage(unsigned long pfn) | |
271 | +{ | |
272 | + int page_order; | |
273 | ||
274 | - mr_len += nr_pages * PAGE_SIZE; | |
275 | - *(unsigned long *)arg = mr_len; | |
276 | + if (pfn & EHEA_HUGEPAGE_PFN_MASK) | |
277 | + return 0; | |
278 | ||
279 | - return 0; | |
280 | + page_order = compound_order(pfn_to_page(pfn)); | |
281 | + if (page_order + PAGE_SHIFT != EHEA_HUGEPAGESHIFT) | |
282 | + return 0; | |
283 | + | |
284 | + return 1; | |
285 | } | |
286 | ||
287 | -static unsigned long ehea_mr_len; | |
288 | +static int ehea_create_busmap_callback(unsigned long initial_pfn, | |
289 | + unsigned long total_nr_pages, void *arg) | |
290 | +{ | |
291 | + int ret; | |
292 | + unsigned long pfn, start_pfn, end_pfn, nr_pages; | |
293 | ||
294 | -static DEFINE_MUTEX(ehea_busmap_mutex); | |
295 | + if ((total_nr_pages * PAGE_SIZE) < EHEA_HUGEPAGE_SIZE) | |
296 | + return ehea_update_busmap(initial_pfn, total_nr_pages, | |
297 | + EHEA_BUSMAP_ADD_SECT); | |
298 | + | |
299 | + /* Given chunk is >= 16GB -> check for hugepages */ | |
300 | + start_pfn = initial_pfn; | |
301 | + end_pfn = initial_pfn + total_nr_pages; | |
302 | + pfn = start_pfn; | |
303 | + | |
304 | + while (pfn < end_pfn) { | |
305 | + if (ehea_is_hugepage(pfn)) { | |
306 | + /* Add mem found in front of the hugepage */ | |
307 | + nr_pages = pfn - start_pfn; | |
308 | + ret = ehea_update_busmap(start_pfn, nr_pages, | |
309 | + EHEA_BUSMAP_ADD_SECT); | |
310 | + if (ret) | |
311 | + return ret; | |
312 | + | |
313 | + /* Skip the hugepage */ | |
314 | + pfn += (EHEA_HUGEPAGE_SIZE / PAGE_SIZE); | |
315 | + start_pfn = pfn; | |
316 | + } else | |
317 | + pfn += (EHEA_SECTSIZE / PAGE_SIZE); | |
318 | + } | |
319 | + | |
320 | + /* Add mem found behind the hugepage(s) */ | |
321 | + nr_pages = pfn - start_pfn; | |
322 | + return ehea_update_busmap(start_pfn, nr_pages, EHEA_BUSMAP_ADD_SECT); | |
323 | +} | |
324 | ||
325 | int ehea_create_busmap(void) | |
326 | { | |
327 | int ret; | |
328 | + | |
329 | mutex_lock(&ehea_busmap_mutex); | |
330 | ehea_mr_len = 0; | |
331 | - ret = walk_memory_resource(0, 1ULL << MAX_PHYSMEM_BITS, &ehea_mr_len, | |
332 | + ret = walk_memory_resource(0, 1ULL << MAX_PHYSMEM_BITS, NULL, | |
333 | ehea_create_busmap_callback); | |
334 | mutex_unlock(&ehea_busmap_mutex); | |
335 | return ret; | |
336 | --- a/drivers/net/ehea/ehea_qmr.h | |
337 | +++ b/drivers/net/ehea/ehea_qmr.h | |
338 | @@ -40,6 +40,9 @@ | |
339 | #define EHEA_PAGESIZE (1UL << EHEA_PAGESHIFT) | |
340 | #define EHEA_SECTSIZE (1UL << 24) | |
341 | #define EHEA_PAGES_PER_SECTION (EHEA_SECTSIZE >> EHEA_PAGESHIFT) | |
342 | +#define EHEA_HUGEPAGESHIFT 34 | |
343 | +#define EHEA_HUGEPAGE_SIZE (1UL << EHEA_HUGEPAGESHIFT) | |
344 | +#define EHEA_HUGEPAGE_PFN_MASK ((EHEA_HUGEPAGE_SIZE - 1) >> PAGE_SHIFT) | |
345 | ||
346 | #if ((1UL << SECTION_SIZE_BITS) < EHEA_SECTSIZE) | |
347 | #error eHEA module cannot work if kernel sectionsize < ehea sectionsize | |
348 | @@ -378,6 +381,8 @@ int ehea_rem_mr(struct ehea_mr *mr); | |
349 | ||
350 | void ehea_error_data(struct ehea_adapter *adapter, u64 res_handle); | |
351 | ||
352 | +int ehea_add_sect_bmap(unsigned long pfn, unsigned long nr_pages); | |
353 | +int ehea_rem_sect_bmap(unsigned long pfn, unsigned long nr_pages); | |
354 | int ehea_create_busmap(void); | |
355 | void ehea_destroy_busmap(void); | |
356 | u64 ehea_map_vaddr(void *caddr); |