summaryrefslogtreecommitdiffstats
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 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/bootmem.h>
+#include <linux/ctype.h>
#include <asm/page.h>
+#include <asm/pgtable.h>
#include <asm/ebcdic.h>
#include <asm/errno.h>
#include <asm/extmem.h>
#include <asm/cpcmd.h>
-#include <linux/ctype.h>
+#include <asm/setup.h>
#define DCSS_DEBUG /* Debug messages on/off */
@@ -77,15 +79,11 @@ struct dcss_segment {
int segcnt;
};
-static DEFINE_SPINLOCK(dcss_lock);
+static DEFINE_MUTEX(dcss_lock);
static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list);
static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
"EW/EN-MIXED" };
-extern struct {
- unsigned long addr, size, type;
-} memory_chunk[MEMORY_CHUNKS];
-
/*
* Create the 8 bytes, ebcdic VM segment name from
* an ascii name.
@@ -117,7 +115,7 @@ segment_by_name (char *name)
struct list_head *l;
struct dcss_segment *tmp, *retval = NULL;
- assert_spin_locked(&dcss_lock);
+ BUG_ON(!mutex_is_locked(&dcss_lock));
dcss_mkname (name, dcss_name);
list_for_each (l, &dcss_list) {
tmp = list_entry (l, struct dcss_segment, list);
@@ -241,65 +239,6 @@ query_segment_type (struct dcss_segment *seg)
}
/*
- * check if the given segment collides with guest storage.
- * returns 1 if this is the case, 0 if no collision was found
- */
-static int
-segment_overlaps_storage(struct dcss_segment *seg)
-{
- int i;
-
- for (i=0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) {
- if (memory_chunk[i].type != 0)
- continue;
- if ((memory_chunk[i].addr >> 20) > (seg->end >> 20))
- continue;
- if (((memory_chunk[i].addr + memory_chunk[i].size - 1) >> 20)
- < (seg->start_addr >> 20))
- continue;
- return 1;
- }
- return 0;
-}
-
-/*
- * check if segment collides with other segments that are currently loaded
- * returns 1 if this is the case, 0 if no collision was found
- */
-static int
-segment_overlaps_others (struct dcss_segment *seg)
-{
- struct list_head *l;
- struct dcss_segment *tmp;
-
- assert_spin_locked(&dcss_lock);
- list_for_each(l, &dcss_list) {
- tmp = list_entry(l, struct dcss_segment, list);
- if ((tmp->start_addr >> 20) > (seg->end >> 20))
- continue;
- if ((tmp->end >> 20) < (seg->start_addr >> 20))
- continue;
- if (seg == tmp)
- continue;
- return 1;
- }
- return 0;
-}
-
-/*
- * check if segment exceeds the kernel mapping range (detected or set via mem=)
- * returns 1 if this is the case, 0 if segment fits into the range
- */
-static inline int
-segment_exceeds_range (struct dcss_segment *seg)
-{
- int seg_last_pfn = (seg->end) >> PAGE_SHIFT;
- if (seg_last_pfn > max_pfn)
- return 1;
- return 0;
-}
-
-/*
* get info about a segment
* possible return values:
* -ENOSYS : we are not running on VM
@@ -344,24 +283,26 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
rc = query_segment_type (seg);
if (rc < 0)
goto out_free;
- if (segment_exceeds_range(seg)) {
- PRINT_WARN ("segment_load: not loading segment %s - exceeds"
- " kernel mapping range\n",name);
- rc = -ERANGE;
+
+ rc = add_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1);
+
+ switch (rc) {
+ case 0:
+ break;
+ case -ENOSPC:
+ PRINT_WARN("segment_load: not loading segment %s - overlaps "
+ "storage/segment\n", name);
goto out_free;
- }
- if (segment_overlaps_storage(seg)) {
- PRINT_WARN ("segment_load: not loading segment %s - overlaps"
- " storage\n",name);
- rc = -ENOSPC;
+ case -ERANGE:
+ PRINT_WARN("segment_load: not loading segment %s - exceeds "
+ "kernel mapping range\n", name);
goto out_free;
- }
- if (segment_overlaps_others(seg)) {
- PRINT_WARN ("segment_load: not loading segment %s - overlaps"
- " other segments\n",name);
- rc = -EBUSY;
+ default:
+ PRINT_WARN("segment_load: not loading segment %s (rc: %d)\n",
+ name, rc);
goto out_free;
}
+
if (do_nonshared)
dcss_command = DCSS_LOADNSR;
else
@@ -375,7 +316,7 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
rc = dcss_diag_translate_rc (seg->end);
dcss_diag(DCSS_PURGESEG, seg->dcss_name,
&seg->start_addr, &seg->end);
- goto out_free;
+ goto out_shared;
}
seg->do_nonshared = do_nonshared;
atomic_set(&seg->ref_count, 1);
@@ -394,6 +335,8 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
(void*)seg->start_addr, (void*)seg->end,
segtype_string[seg->vm_segtype]);
goto out;
+ out_shared:
+ remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1);
out_free:
kfree(seg);
out:
@@ -429,7 +372,7 @@ segment_load (char *name, int do_nonshared, unsigned long *addr,
if (!MACHINE_IS_VM)
return -ENOSYS;
- spin_lock (&dcss_lock);
+ mutex_lock(&dcss_lock);
seg = segment_by_name (name);
if (seg == NULL)
rc = __segment_load (name, do_nonshared, addr, end);
@@ -444,7 +387,7 @@ segment_load (char *name, int do_nonshared, unsigned long *addr,
rc = -EPERM;
}
}
- spin_unlock (&dcss_lock);
+ mutex_unlock(&dcss_lock);
return rc;
}
@@ -467,7 +410,7 @@ segment_modify_shared (char *name, int do_nonshared)
unsigned long dummy;
int dcss_command, rc, diag_cc;
- spin_lock (&dcss_lock);
+ mutex_lock(&dcss_lock);
seg = segment_by_name (name);
if (seg == NULL) {
rc = -EINVAL;
@@ -508,7 +451,7 @@ segment_modify_shared (char *name, int do_nonshared)
&dummy, &dummy);
kfree(seg);
out_unlock:
- spin_unlock(&dcss_lock);
+ mutex_unlock(&dcss_lock);
return rc;
}
@@ -526,21 +469,21 @@ segment_unload(char *name)
if (!MACHINE_IS_VM)
return;
- spin_lock(&dcss_lock);
+ mutex_lock(&dcss_lock);
seg = segment_by_name (name);
if (seg == NULL) {
PRINT_ERR ("could not find segment %s in segment_unload, "
"please report to linux390@de.ibm.com\n",name);
goto out_unlock;
}
- if (atomic_dec_return(&seg->ref_count) == 0) {
- list_del(&seg->list);
- dcss_diag(DCSS_PURGESEG, seg->dcss_name,
- &dummy, &dummy);
- kfree(seg);
- }
+ if (atomic_dec_return(&seg->ref_count) != 0)
+ goto out_unlock;
+ remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1);
+ list_del(&seg->list);
+ dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
+ kfree(seg);
out_unlock:
- spin_unlock(&dcss_lock);
+ mutex_unlock(&dcss_lock);
}
/*
@@ -559,12 +502,13 @@ segment_save(char *name)
if (!MACHINE_IS_VM)
return;
- spin_lock(&dcss_lock);
+ mutex_lock(&dcss_lock);
seg = segment_by_name (name);
if (seg == NULL) {
- PRINT_ERR ("could not find segment %s in segment_save, please report to linux390@de.ibm.com\n",name);
- return;
+ PRINT_ERR("could not find segment %s in segment_save, please "
+ "report to linux390@de.ibm.com\n", name);
+ goto out;
}
startpfn = seg->start_addr >> PAGE_SHIFT;
@@ -591,7 +535,7 @@ segment_save(char *name)
goto out;
}
out:
- spin_unlock(&dcss_lock);
+ mutex_unlock(&dcss_lock);
}
EXPORT_SYMBOL(segment_load);