diff options
Diffstat (limited to 'arch/s390/mm/extmem.c')
-rw-r--r-- | arch/s390/mm/extmem.c | 138 |
1 files changed, 41 insertions, 97 deletions
diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c index 226275d5c4f6..775bf19e742b 100644 --- a/arch/s390/mm/extmem.c +++ b/arch/s390/mm/extmem.c | |||
@@ -14,12 +14,14 @@ | |||
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/bootmem.h> | 16 | #include <linux/bootmem.h> |
17 | #include <linux/ctype.h> | ||
17 | #include <asm/page.h> | 18 | #include <asm/page.h> |
19 | #include <asm/pgtable.h> | ||
18 | #include <asm/ebcdic.h> | 20 | #include <asm/ebcdic.h> |
19 | #include <asm/errno.h> | 21 | #include <asm/errno.h> |
20 | #include <asm/extmem.h> | 22 | #include <asm/extmem.h> |
21 | #include <asm/cpcmd.h> | 23 | #include <asm/cpcmd.h> |
22 | #include <linux/ctype.h> | 24 | #include <asm/setup.h> |
23 | 25 | ||
24 | #define DCSS_DEBUG /* Debug messages on/off */ | 26 | #define DCSS_DEBUG /* Debug messages on/off */ |
25 | 27 | ||
@@ -77,15 +79,11 @@ struct dcss_segment { | |||
77 | int segcnt; | 79 | int segcnt; |
78 | }; | 80 | }; |
79 | 81 | ||
80 | static DEFINE_SPINLOCK(dcss_lock); | 82 | static DEFINE_MUTEX(dcss_lock); |
81 | static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list); | 83 | static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list); |
82 | static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", | 84 | static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", |
83 | "EW/EN-MIXED" }; | 85 | "EW/EN-MIXED" }; |
84 | 86 | ||
85 | extern struct { | ||
86 | unsigned long addr, size, type; | ||
87 | } memory_chunk[MEMORY_CHUNKS]; | ||
88 | |||
89 | /* | 87 | /* |
90 | * Create the 8 bytes, ebcdic VM segment name from | 88 | * Create the 8 bytes, ebcdic VM segment name from |
91 | * an ascii name. | 89 | * an ascii name. |
@@ -117,7 +115,7 @@ segment_by_name (char *name) | |||
117 | struct list_head *l; | 115 | struct list_head *l; |
118 | struct dcss_segment *tmp, *retval = NULL; | 116 | struct dcss_segment *tmp, *retval = NULL; |
119 | 117 | ||
120 | assert_spin_locked(&dcss_lock); | 118 | BUG_ON(!mutex_is_locked(&dcss_lock)); |
121 | dcss_mkname (name, dcss_name); | 119 | dcss_mkname (name, dcss_name); |
122 | list_for_each (l, &dcss_list) { | 120 | list_for_each (l, &dcss_list) { |
123 | tmp = list_entry (l, struct dcss_segment, list); | 121 | tmp = list_entry (l, struct dcss_segment, list); |
@@ -241,65 +239,6 @@ query_segment_type (struct dcss_segment *seg) | |||
241 | } | 239 | } |
242 | 240 | ||
243 | /* | 241 | /* |
244 | * check if the given segment collides with guest storage. | ||
245 | * returns 1 if this is the case, 0 if no collision was found | ||
246 | */ | ||
247 | static int | ||
248 | segment_overlaps_storage(struct dcss_segment *seg) | ||
249 | { | ||
250 | int i; | ||
251 | |||
252 | for (i=0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { | ||
253 | if (memory_chunk[i].type != 0) | ||
254 | continue; | ||
255 | if ((memory_chunk[i].addr >> 20) > (seg->end >> 20)) | ||
256 | continue; | ||
257 | if (((memory_chunk[i].addr + memory_chunk[i].size - 1) >> 20) | ||
258 | < (seg->start_addr >> 20)) | ||
259 | continue; | ||
260 | return 1; | ||
261 | } | ||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * check if segment collides with other segments that are currently loaded | ||
267 | * returns 1 if this is the case, 0 if no collision was found | ||
268 | */ | ||
269 | static int | ||
270 | segment_overlaps_others (struct dcss_segment *seg) | ||
271 | { | ||
272 | struct list_head *l; | ||
273 | struct dcss_segment *tmp; | ||
274 | |||
275 | assert_spin_locked(&dcss_lock); | ||
276 | list_for_each(l, &dcss_list) { | ||
277 | tmp = list_entry(l, struct dcss_segment, list); | ||
278 | if ((tmp->start_addr >> 20) > (seg->end >> 20)) | ||
279 | continue; | ||
280 | if ((tmp->end >> 20) < (seg->start_addr >> 20)) | ||
281 | continue; | ||
282 | if (seg == tmp) | ||
283 | continue; | ||
284 | return 1; | ||
285 | } | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | /* | ||
290 | * check if segment exceeds the kernel mapping range (detected or set via mem=) | ||
291 | * returns 1 if this is the case, 0 if segment fits into the range | ||
292 | */ | ||
293 | static inline int | ||
294 | segment_exceeds_range (struct dcss_segment *seg) | ||
295 | { | ||
296 | int seg_last_pfn = (seg->end) >> PAGE_SHIFT; | ||
297 | if (seg_last_pfn > max_pfn) | ||
298 | return 1; | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * get info about a segment | 242 | * get info about a segment |
304 | * possible return values: | 243 | * possible return values: |
305 | * -ENOSYS : we are not running on VM | 244 | * -ENOSYS : we are not running on VM |
@@ -344,24 +283,26 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long | |||
344 | rc = query_segment_type (seg); | 283 | rc = query_segment_type (seg); |
345 | if (rc < 0) | 284 | if (rc < 0) |
346 | goto out_free; | 285 | goto out_free; |
347 | if (segment_exceeds_range(seg)) { | 286 | |
348 | PRINT_WARN ("segment_load: not loading segment %s - exceeds" | 287 | rc = add_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); |
349 | " kernel mapping range\n",name); | 288 | |
350 | 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); | ||
351 | goto out_free; | 295 | goto out_free; |
352 | } | 296 | case -ERANGE: |
353 | if (segment_overlaps_storage(seg)) { | 297 | PRINT_WARN("segment_load: not loading segment %s - exceeds " |
354 | PRINT_WARN ("segment_load: not loading segment %s - overlaps" | 298 | "kernel mapping range\n", name); |
355 | " storage\n",name); | ||
356 | rc = -ENOSPC; | ||
357 | goto out_free; | 299 | goto out_free; |
358 | } | 300 | default: |
359 | if (segment_overlaps_others(seg)) { | 301 | PRINT_WARN("segment_load: not loading segment %s (rc: %d)\n", |
360 | PRINT_WARN ("segment_load: not loading segment %s - overlaps" | 302 | name, rc); |
361 | " other segments\n",name); | ||
362 | rc = -EBUSY; | ||
363 | goto out_free; | 303 | goto out_free; |
364 | } | 304 | } |
305 | |||
365 | if (do_nonshared) | 306 | if (do_nonshared) |
366 | dcss_command = DCSS_LOADNSR; | 307 | dcss_command = DCSS_LOADNSR; |
367 | else | 308 | else |
@@ -375,7 +316,7 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long | |||
375 | rc = dcss_diag_translate_rc (seg->end); | 316 | rc = dcss_diag_translate_rc (seg->end); |
376 | dcss_diag(DCSS_PURGESEG, seg->dcss_name, | 317 | dcss_diag(DCSS_PURGESEG, seg->dcss_name, |
377 | &seg->start_addr, &seg->end); | 318 | &seg->start_addr, &seg->end); |
378 | goto out_free; | 319 | goto out_shared; |
379 | } | 320 | } |
380 | seg->do_nonshared = do_nonshared; | 321 | seg->do_nonshared = do_nonshared; |
381 | atomic_set(&seg->ref_count, 1); | 322 | atomic_set(&seg->ref_count, 1); |
@@ -394,6 +335,8 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long | |||
394 | (void*)seg->start_addr, (void*)seg->end, | 335 | (void*)seg->start_addr, (void*)seg->end, |
395 | segtype_string[seg->vm_segtype]); | 336 | segtype_string[seg->vm_segtype]); |
396 | goto out; | 337 | goto out; |
338 | out_shared: | ||
339 | remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); | ||
397 | out_free: | 340 | out_free: |
398 | kfree(seg); | 341 | kfree(seg); |
399 | out: | 342 | out: |
@@ -429,7 +372,7 @@ segment_load (char *name, int do_nonshared, unsigned long *addr, | |||
429 | if (!MACHINE_IS_VM) | 372 | if (!MACHINE_IS_VM) |
430 | return -ENOSYS; | 373 | return -ENOSYS; |
431 | 374 | ||
432 | spin_lock (&dcss_lock); | 375 | mutex_lock(&dcss_lock); |
433 | seg = segment_by_name (name); | 376 | seg = segment_by_name (name); |
434 | if (seg == NULL) | 377 | if (seg == NULL) |
435 | rc = __segment_load (name, do_nonshared, addr, end); | 378 | rc = __segment_load (name, do_nonshared, addr, end); |
@@ -444,7 +387,7 @@ segment_load (char *name, int do_nonshared, unsigned long *addr, | |||
444 | rc = -EPERM; | 387 | rc = -EPERM; |
445 | } | 388 | } |
446 | } | 389 | } |
447 | spin_unlock (&dcss_lock); | 390 | mutex_unlock(&dcss_lock); |
448 | return rc; | 391 | return rc; |
449 | } | 392 | } |
450 | 393 | ||
@@ -467,7 +410,7 @@ segment_modify_shared (char *name, int do_nonshared) | |||
467 | unsigned long dummy; | 410 | unsigned long dummy; |
468 | int dcss_command, rc, diag_cc; | 411 | int dcss_command, rc, diag_cc; |
469 | 412 | ||
470 | spin_lock (&dcss_lock); | 413 | mutex_lock(&dcss_lock); |
471 | seg = segment_by_name (name); | 414 | seg = segment_by_name (name); |
472 | if (seg == NULL) { | 415 | if (seg == NULL) { |
473 | rc = -EINVAL; | 416 | rc = -EINVAL; |
@@ -508,7 +451,7 @@ segment_modify_shared (char *name, int do_nonshared) | |||
508 | &dummy, &dummy); | 451 | &dummy, &dummy); |
509 | kfree(seg); | 452 | kfree(seg); |
510 | out_unlock: | 453 | out_unlock: |
511 | spin_unlock(&dcss_lock); | 454 | mutex_unlock(&dcss_lock); |
512 | return rc; | 455 | return rc; |
513 | } | 456 | } |
514 | 457 | ||
@@ -526,21 +469,21 @@ segment_unload(char *name) | |||
526 | if (!MACHINE_IS_VM) | 469 | if (!MACHINE_IS_VM) |
527 | return; | 470 | return; |
528 | 471 | ||
529 | spin_lock(&dcss_lock); | 472 | mutex_lock(&dcss_lock); |
530 | seg = segment_by_name (name); | 473 | seg = segment_by_name (name); |
531 | if (seg == NULL) { | 474 | if (seg == NULL) { |
532 | PRINT_ERR ("could not find segment %s in segment_unload, " | 475 | PRINT_ERR ("could not find segment %s in segment_unload, " |
533 | "please report to linux390@de.ibm.com\n",name); | 476 | "please report to linux390@de.ibm.com\n",name); |
534 | goto out_unlock; | 477 | goto out_unlock; |
535 | } | 478 | } |
536 | if (atomic_dec_return(&seg->ref_count) == 0) { | 479 | if (atomic_dec_return(&seg->ref_count) != 0) |
537 | list_del(&seg->list); | 480 | goto out_unlock; |
538 | dcss_diag(DCSS_PURGESEG, seg->dcss_name, | 481 | remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1); |
539 | &dummy, &dummy); | 482 | list_del(&seg->list); |
540 | kfree(seg); | 483 | dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); |
541 | } | 484 | kfree(seg); |
542 | out_unlock: | 485 | out_unlock: |
543 | spin_unlock(&dcss_lock); | 486 | mutex_unlock(&dcss_lock); |
544 | } | 487 | } |
545 | 488 | ||
546 | /* | 489 | /* |
@@ -559,12 +502,13 @@ segment_save(char *name) | |||
559 | if (!MACHINE_IS_VM) | 502 | if (!MACHINE_IS_VM) |
560 | return; | 503 | return; |
561 | 504 | ||
562 | spin_lock(&dcss_lock); | 505 | mutex_lock(&dcss_lock); |
563 | seg = segment_by_name (name); | 506 | seg = segment_by_name (name); |
564 | 507 | ||
565 | if (seg == NULL) { | 508 | if (seg == NULL) { |
566 | PRINT_ERR ("could not find segment %s in segment_save, please report to linux390@de.ibm.com\n",name); | 509 | PRINT_ERR("could not find segment %s in segment_save, please " |
567 | return; | 510 | "report to linux390@de.ibm.com\n", name); |
511 | goto out; | ||
568 | } | 512 | } |
569 | 513 | ||
570 | startpfn = seg->start_addr >> PAGE_SHIFT; | 514 | startpfn = seg->start_addr >> PAGE_SHIFT; |
@@ -591,7 +535,7 @@ segment_save(char *name) | |||
591 | goto out; | 535 | goto out; |
592 | } | 536 | } |
593 | out: | 537 | out: |
594 | spin_unlock(&dcss_lock); | 538 | mutex_unlock(&dcss_lock); |
595 | } | 539 | } |
596 | 540 | ||
597 | EXPORT_SYMBOL(segment_load); | 541 | EXPORT_SYMBOL(segment_load); |