aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/mm/extmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/mm/extmem.c')
-rw-r--r--arch/s390/mm/extmem.c138
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
80static DEFINE_SPINLOCK(dcss_lock); 82static DEFINE_MUTEX(dcss_lock);
81static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list); 83static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list);
82static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", 84static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
83 "EW/EN-MIXED" }; 85 "EW/EN-MIXED" };
84 86
85extern 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 */
247static int
248segment_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 */
269static int
270segment_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 */
293static inline int
294segment_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);
542out_unlock: 485out_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 }
593out: 537out:
594 spin_unlock(&dcss_lock); 538 mutex_unlock(&dcss_lock);
595} 539}
596 540
597EXPORT_SYMBOL(segment_load); 541EXPORT_SYMBOL(segment_load);