diff options
author | Heiko Carstens <heiko.carstens@de.ibm.com> | 2006-12-08 09:56:07 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2006-12-08 09:56:07 -0500 |
commit | f4eb07c17df2e6cf9bd58bfcd9cc9e05e9489d07 (patch) | |
tree | c1b4b422d3b8183edf452cc745dadd0fe129018b /arch/s390/mm/extmem.c | |
parent | 7f090145a14afc35844dce80174c9c24f9e66ec5 (diff) |
[S390] Virtual memmap for s390.
Virtual memmap support for s390. Inspired by the ia64 implementation.
Unlike ia64 we need a mechanism which allows us to dynamically attach
shared memory regions.
These memory regions are accessed via the dcss device driver. dcss
implements the 'direct_access' operation, which requires struct pages
for every single shared page.
Therefore this implementation provides an interface to attach/detach
shared memory:
int add_shared_memory(unsigned long start, unsigned long size);
int remove_shared_memory(unsigned long start, unsigned long size);
The purpose of the add_shared_memory function is to add the given
memory range to the 1:1 mapping and to make sure that the
corresponding range in the vmemmap is backed with physical pages.
It also initialises the new struct pages.
remove_shared_memory in turn only invalidates the page table
entries in the 1:1 mapping. The page tables and the memory used for
struct pages in the vmemmap are currently not freed. They will be
reused when the next segment will be attached.
Given that the maximum size of a shared memory region is 2GB and
in addition all regions must reside below 2GB this is not too much of
a restriction, but there is room for improvement.
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/mm/extmem.c')
-rw-r--r-- | arch/s390/mm/extmem.c | 106 |
1 files changed, 26 insertions, 80 deletions
diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c index 9e9bc48463a5..775bf19e742b 100644 --- a/arch/s390/mm/extmem.c +++ b/arch/s390/mm/extmem.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/bootmem.h> | 16 | #include <linux/bootmem.h> |
17 | #include <linux/ctype.h> | 17 | #include <linux/ctype.h> |
18 | #include <asm/page.h> | 18 | #include <asm/page.h> |
19 | #include <asm/pgtable.h> | ||
19 | #include <asm/ebcdic.h> | 20 | #include <asm/ebcdic.h> |
20 | #include <asm/errno.h> | 21 | #include <asm/errno.h> |
21 | #include <asm/extmem.h> | 22 | #include <asm/extmem.h> |
@@ -238,65 +239,6 @@ query_segment_type (struct dcss_segment *seg) | |||
238 | } | 239 | } |
239 | 240 | ||
240 | /* | 241 | /* |
241 | * check if the given segment collides with guest storage. | ||
242 | * returns 1 if this is the case, 0 if no collision was found | ||
243 | */ | ||
244 | static int | ||
245 | segment_overlaps_storage(struct dcss_segment *seg) | ||
246 | { | ||
247 | int i; | ||
248 | |||
249 | for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { | ||
250 | if (memory_chunk[i].type != CHUNK_READ_WRITE) | ||
251 | continue; | ||
252 | if ((memory_chunk[i].addr >> 20) > (seg->end >> 20)) | ||
253 | continue; | ||
254 | if (((memory_chunk[i].addr + memory_chunk[i].size - 1) >> 20) | ||
255 | < (seg->start_addr >> 20)) | ||
256 | continue; | ||
257 | return 1; | ||
258 | } | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * check if segment collides with other segments that are currently loaded | ||
264 | * returns 1 if this is the case, 0 if no collision was found | ||
265 | */ | ||
266 | static int | ||
267 | segment_overlaps_others (struct dcss_segment *seg) | ||
268 | { | ||
269 | struct list_head *l; | ||
270 | struct dcss_segment *tmp; | ||
271 | |||
272 | BUG_ON(!mutex_is_locked(&dcss_lock)); | ||
273 | list_for_each(l, &dcss_list) { | ||
274 | tmp = list_entry(l, struct dcss_segment, list); | ||
275 | if ((tmp->start_addr >> 20) > (seg->end >> 20)) | ||
276 | continue; | ||
277 | if ((tmp->end >> 20) < (seg->start_addr >> 20)) | ||
278 | continue; | ||
279 | if (seg == tmp) | ||
280 | continue; | ||
281 | return 1; | ||
282 | } | ||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | /* | ||
287 | * check if segment exceeds the kernel mapping range (detected or set via mem=) | ||
288 | * returns 1 if this is the case, 0 if segment fits into the range | ||
289 | */ | ||
290 | static inline int | ||
291 | segment_exceeds_range (struct dcss_segment *seg) | ||
292 | { | ||
293 | int seg_last_pfn = (seg->end) >> PAGE_SHIFT; | ||
294 | if (seg_last_pfn > max_pfn) | ||
295 | return 1; | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * get info about a segment | 242 | * get info about a segment |
301 | * possible return values: | 243 | * possible return values: |
302 | * -ENOSYS : we are not running on VM | 244 | * -ENOSYS : we are not running on VM |
@@ -341,24 +283,26 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long | |||
341 | rc = query_segment_type (seg); | 283 | rc = query_segment_type (seg); |
342 | if (rc < 0) | 284 | if (rc < 0) |
343 | goto out_free; | 285 | goto out_free; |
344 | if (segment_exceeds_range(seg)) { | 286 | |
345 | PRINT_WARN ("segment_load: not loading segment %s - exceeds" | 287 | rc = add_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); |
346 | " kernel mapping range\n",name); | 288 | |
347 | rc = -ERANGE; | 289 | switch (rc) { |
290 | case 0: | ||
291 | break; | ||
292 | case -ENOSPC: | ||
293 | PRINT_WARN("segment_load: not loading segment %s - overlaps " | ||
294 | "storage/segment\n", name); | ||
348 | goto out_free; | 295 | goto out_free; |
349 | } | 296 | case -ERANGE: |
350 | if (segment_overlaps_storage(seg)) { | 297 | PRINT_WARN("segment_load: not loading segment %s - exceeds " |
351 | PRINT_WARN ("segment_load: not loading segment %s - overlaps" | 298 | "kernel mapping range\n", name); |
352 | " storage\n",name); | ||
353 | rc = -ENOSPC; | ||
354 | goto out_free; | 299 | goto out_free; |
355 | } | 300 | default: |
356 | if (segment_overlaps_others(seg)) { | 301 | PRINT_WARN("segment_load: not loading segment %s (rc: %d)\n", |
357 | PRINT_WARN ("segment_load: not loading segment %s - overlaps" | 302 | name, rc); |
358 | " other segments\n",name); | ||
359 | rc = -EBUSY; | ||
360 | goto out_free; | 303 | goto out_free; |
361 | } | 304 | } |
305 | |||
362 | if (do_nonshared) | 306 | if (do_nonshared) |
363 | dcss_command = DCSS_LOADNSR; | 307 | dcss_command = DCSS_LOADNSR; |
364 | else | 308 | else |
@@ -372,7 +316,7 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long | |||
372 | rc = dcss_diag_translate_rc (seg->end); | 316 | rc = dcss_diag_translate_rc (seg->end); |
373 | dcss_diag(DCSS_PURGESEG, seg->dcss_name, | 317 | dcss_diag(DCSS_PURGESEG, seg->dcss_name, |
374 | &seg->start_addr, &seg->end); | 318 | &seg->start_addr, &seg->end); |
375 | goto out_free; | 319 | goto out_shared; |
376 | } | 320 | } |
377 | seg->do_nonshared = do_nonshared; | 321 | seg->do_nonshared = do_nonshared; |
378 | atomic_set(&seg->ref_count, 1); | 322 | atomic_set(&seg->ref_count, 1); |
@@ -391,6 +335,8 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long | |||
391 | (void*)seg->start_addr, (void*)seg->end, | 335 | (void*)seg->start_addr, (void*)seg->end, |
392 | segtype_string[seg->vm_segtype]); | 336 | segtype_string[seg->vm_segtype]); |
393 | goto out; | 337 | goto out; |
338 | out_shared: | ||
339 | remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); | ||
394 | out_free: | 340 | out_free: |
395 | kfree(seg); | 341 | kfree(seg); |
396 | out: | 342 | out: |
@@ -530,12 +476,12 @@ segment_unload(char *name) | |||
530 | "please report to linux390@de.ibm.com\n",name); | 476 | "please report to linux390@de.ibm.com\n",name); |
531 | goto out_unlock; | 477 | goto out_unlock; |
532 | } | 478 | } |
533 | if (atomic_dec_return(&seg->ref_count) == 0) { | 479 | if (atomic_dec_return(&seg->ref_count) != 0) |
534 | list_del(&seg->list); | 480 | goto out_unlock; |
535 | dcss_diag(DCSS_PURGESEG, seg->dcss_name, | 481 | remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); |
536 | &dummy, &dummy); | 482 | list_del(&seg->list); |
537 | kfree(seg); | 483 | dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); |
538 | } | 484 | kfree(seg); |
539 | out_unlock: | 485 | out_unlock: |
540 | mutex_unlock(&dcss_lock); | 486 | mutex_unlock(&dcss_lock); |
541 | } | 487 | } |