mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [patch 3/6] s390: dcss segments.
@ 2004-11-30 15:09 Martin Schwidefsky
  0 siblings, 0 replies; only message in thread
From: Martin Schwidefsky @ 2004-11-30 15:09 UTC (permalink / raw)
  To: akpm, linux-kernel

[patch 3/6] s390: dcss segments.

From: Carsten Otte <cotte@de.ibm.com>

dcss segment interface changes:
 - Add check when loading segments to avoid out of range mappings.
 - Add code to check for segment_load returning -ERANGE.
 - Rename segment_info to segment_type.
 - Restore previous segment state if reload fails.
 - Add segment_modify_shared() to change shared attributes of a dcss
   segment and use it in the dcss block device driver.
 - Add support for contiguous EW/EN multipart segments.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

diffstat:
 arch/s390/mm/extmem.c         |  159 +++++++++++++++++++++++++++++++++++++-----
 drivers/s390/block/dcssblk.c  |   69 ++++++++----------
 drivers/s390/char/monreader.c |    8 +-
 include/asm-s390/extmem.h     |    4 -
 4 files changed, 183 insertions(+), 57 deletions(-)

diff -urN linux-2.6/arch/s390/mm/extmem.c linux-2.6-patched/arch/s390/mm/extmem.c
--- linux-2.6/arch/s390/mm/extmem.c	2004-11-30 14:03:01.000000000 +0100
+++ linux-2.6-patched/arch/s390/mm/extmem.c	2004-11-30 14:03:19.000000000 +0100
@@ -40,14 +40,19 @@
 #define DCSS_FINDSEG    0x0c
 #define DCSS_LOADNOLY   0x10
 #define DCSS_SEGEXT     0x18
-#define DCSS_QACTV      0x0c
+#define DCSS_FINDSEGA   0x0c
+
+struct qrange {
+	unsigned int  start; // 3byte start address, 1 byte type
+	unsigned int  end;   // 3byte end address, 1 byte reserved
+};
 
 struct qout64 {
 	int segstart;
 	int segend;
 	int segcnt;
 	int segrcnt;
-	char segout[8][6];
+	struct qrange range[6];
 };
 
 struct qin64 {
@@ -67,12 +72,15 @@
 	unsigned long end;
 	atomic_t ref_count;
 	int do_nonshared;
-	int vm_segtype;
+	unsigned int vm_segtype;
+	struct qrange range[6];
+	int segcnt;
 };
 
 static spinlock_t dcss_lock = SPIN_LOCK_UNLOCKED;
 static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list);
-static char *segtype_string[7] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC" };
+static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
+					"EW/EN-MIXED" };
 
 extern struct {
 	unsigned long addr, size, type;
@@ -162,12 +170,12 @@
  * fills start_address, end and vm_segtype fields
  */
 static int
-query_segment_info (struct dcss_segment *seg)
+query_segment_type (struct dcss_segment *seg)
 {
 	struct qin64  *qin = kmalloc (sizeof(struct qin64), GFP_DMA);
 	struct qout64 *qout = kmalloc (sizeof(struct qout64), GFP_DMA);
 
-	int diag_cc, rc;
+	int diag_cc, rc, i;
 	unsigned long dummy, vmrc;
 
 	if ((qin == NULL) || (qout == NULL)) {
@@ -176,7 +184,7 @@
 	}
 
 	/* initialize diag input parameters */
-	qin->qopcode = DCSS_QACTV;
+	qin->qopcode = DCSS_FINDSEGA;
 	qin->qoutptr = (unsigned long) qout;
 	qin->qoutlen = sizeof(struct qout64);
 	memcpy (qin->qname, seg->dcss_name, 8);
@@ -188,16 +196,40 @@
 		goto out_free;
 	}
 
-	if (qout->segcnt > 1) {
+	if (qout->segcnt > 6) {
 		rc = -ENOTSUPP;
 		goto out_free;
 	}
 
+	if (qout->segcnt == 1) {
+		seg->vm_segtype = qout->range[0].start & 0xff;
+	} else {
+		/* multi-part segment. only one type supported here:
+		    - all parts are contiguous
+		    - all parts are either EW or EN type
+		    - maximum 6 parts allowed */
+		unsigned long start = qout->segstart >> PAGE_SHIFT;
+		for (i=0; i<qout->segcnt; i++) {
+			if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) &&
+			    ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) {
+				rc = -ENOTSUPP;
+				goto out_free;
+			}
+			if (start != qout->range[i].start >> PAGE_SHIFT) {
+				rc = -ENOTSUPP;
+				goto out_free;
+			}
+			start = (qout->range[i].end >> PAGE_SHIFT) + 1;
+		}
+		seg->vm_segtype = SEG_TYPE_EWEN;
+	}
+
 	/* analyze diag output and update seg */
 	seg->start_addr = qout->segstart;
 	seg->end = qout->segend;
 
-	seg->vm_segtype = qout->segout[0][3];
+	memcpy (seg->range, qout->range, 6*sizeof(struct qrange));
+	seg->segcnt = qout->segcnt;
 
 	rc = 0;
 
@@ -254,6 +286,19 @@
 }
 
 /*
+ * 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
@@ -265,7 +310,7 @@
  * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
  */
 int
-segment_info (char* name)
+segment_type (char* name)
 {
 	int rc;
 	struct dcss_segment seg;
@@ -274,7 +319,7 @@
 		return -ENOSYS;
 
 	dcss_mkname(name, seg.dcss_name);
-	rc = query_segment_info (&seg);
+	rc = query_segment_type (&seg);
 	if (rc < 0)
 		return rc;
 	return seg.vm_segtype;
@@ -295,9 +340,15 @@
 		goto out;
 	}
 	dcss_mkname (name, seg->dcss_name);
-	rc = query_segment_info (seg);
+	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;
+		goto out_free;
+	}
 	if (segment_overlaps_storage(seg)) {
 		PRINT_WARN ("segment_load: not loading segment %s - overlaps"
 				" storage\n",name);
@@ -362,6 +413,7 @@
  * -ENOTSUPP: multi-part segment cannot be used with linux
  * -ENOSPC  : segment cannot be used (overlaps with storage)
  * -EBUSY   : segment can temporarily not be used (overlaps with dcss)
+ * -ERANGE  : segment cannot be used (exceeds kernel mapping range)
  * -EPERM   : segment is currently loaded with incompatible permissions
  * -ENOMEM  : out of memory
  * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
@@ -396,6 +448,70 @@
 }
 
 /*
+ * this function modifies the shared state of a DCSS segment. note that
+ * name         : name of the DCSS
+ * do_nonshared : 0 indicates that the dcss should be shared with other linux images
+ *                1 indicates that the dcss should be exclusive for this linux image
+ * return values:
+ * -EIO     : could not perform load diagnose (segment gone!)
+ * -ENOENT  : no such segment (segment gone!)
+ * -EAGAIN  : segment is in use by other exploiters, try later
+ * -EINVAL  : no segment with the given name is currently loaded - name invalid
+ * 0	    : operation succeeded
+ */
+int
+segment_modify_shared (char *name, int do_nonshared)
+{
+	struct dcss_segment *seg;
+	unsigned long dummy;
+	int dcss_command, rc, diag_cc;
+
+	spin_lock (&dcss_lock);
+	seg = segment_by_name (name);
+	if (seg == NULL) {
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+	if (do_nonshared == seg->do_nonshared) {
+		PRINT_INFO ("segment_modify_shared: not reloading segment %s"
+				" - already in requested mode\n",name);
+		rc = 0;
+		goto out_unlock;
+	}
+	if (atomic_read (&seg->ref_count) != 1) {
+		PRINT_WARN ("segment_modify_shared: not reloading segment %s - "
+				"segment is in use by other driver(s)\n",name);
+		rc = -EAGAIN;
+		goto out_unlock;
+	}
+	dcss_diag(DCSS_PURGESEG, seg->dcss_name,
+		  &dummy, &dummy);
+	if (do_nonshared)
+		dcss_command = DCSS_LOADNSR;
+	else
+	dcss_command = DCSS_LOADNOLY;
+	diag_cc = dcss_diag(dcss_command, seg->dcss_name,
+			&seg->start_addr, &seg->end);
+	if (diag_cc > 1) {
+		PRINT_WARN ("segment_modify_shared: could not reload segment %s"
+				" - diag returned error (%ld)\n",name,seg->end);
+		rc = dcss_diag_translate_rc (seg->end);
+		goto out_del;
+	}
+	seg->do_nonshared = do_nonshared;
+	rc = 0;
+	goto out_unlock;
+ out_del:
+	list_del(&seg->list);
+	dcss_diag(DCSS_PURGESEG, seg->dcss_name,
+		  &dummy, &dummy);
+	kfree (seg);
+ out_unlock:
+	spin_unlock(&dcss_lock);
+	return rc;
+}
+
+/*
  * Decrease the use count of a DCSS segment and remove
  * it from the address space if nobody is using it
  * any longer.
@@ -434,8 +550,9 @@
 	struct dcss_segment *seg;
 	int startpfn = 0;
 	int endpfn = 0;
-	char cmd1[80];
+	char cmd1[160];
 	char cmd2[80];
+	int i;
 
 	if (!MACHINE_IS_VM)
 		return;
@@ -448,10 +565,15 @@
 		return;
 	}
 
-	startpfn = seg->start_addr >> 12;
-	endpfn = (seg->end) >> 12;
-	sprintf(cmd1, "DEFSEG %s %X-%X %s", name, startpfn, endpfn,
-			segtype_string[seg->vm_segtype]);
+	startpfn = seg->start_addr >> PAGE_SHIFT;
+	endpfn = (seg->end) >> PAGE_SHIFT;
+	sprintf(cmd1, "DEFSEG %s", name);
+	for (i=0; i<seg->segcnt; i++) {
+		sprintf(cmd1+strlen(cmd1), " %X-%X %s",
+			seg->range[i].start >> PAGE_SHIFT,
+			seg->range[i].end >> PAGE_SHIFT,
+			segtype_string[seg->range[i].start & 0xff]);
+	}
 	sprintf(cmd2, "SAVESEG %s", name);
 	cpcmd(cmd1, NULL, 80);
 	cpcmd(cmd2, NULL, 80);
@@ -461,4 +583,5 @@
 EXPORT_SYMBOL(segment_load);
 EXPORT_SYMBOL(segment_unload);
 EXPORT_SYMBOL(segment_save);
-EXPORT_SYMBOL(segment_info);
+EXPORT_SYMBOL(segment_type);
+EXPORT_SYMBOL(segment_modify_shared);
diff -urN linux-2.6/drivers/s390/block/dcssblk.c linux-2.6-patched/drivers/s390/block/dcssblk.c
--- linux-2.6/drivers/s390/block/dcssblk.c	2004-11-30 14:03:04.000000000 +0100
+++ linux-2.6-patched/drivers/s390/block/dcssblk.c	2004-11-30 14:03:19.000000000 +0100
@@ -140,7 +140,7 @@
 }
 
 /*
- * print appropriate error message for segment_load()/segment_info()
+ * print appropriate error message for segment_load()/segment_type()
  * return code
  */
 static void
@@ -179,6 +179,10 @@
 		PRINT_WARN("cannot load/query segment %s, out of memory\n",
 			   seg_name);
 		break;
+	case -ERANGE:
+		PRINT_WARN("cannot load/query segment %s, exceeds kernel "
+			   "mapping range\n", seg_name);
+		break;
 	default:
 		PRINT_WARN("cannot load/query segment %s, return value %i\n",
 			   seg_name, rc);
@@ -214,57 +218,50 @@
 	if (atomic_read(&dev_info->use_count)) {
 		PRINT_ERR("share: segment %s is busy!\n",
 			  dev_info->segment_name);
-		up_write(&dcssblk_devices_sem);
-		return -EBUSY;
-	}
-	if ((inbuf[0] == '1') && (dev_info->is_shared == 1)) {
-		PRINT_WARN("Segment %s already loaded in shared mode!\n",
-			   dev_info->segment_name);
-		up_write(&dcssblk_devices_sem);
-		return count;
-	}
-	if ((inbuf[0] == '0') && (dev_info->is_shared == 0)) {
-		PRINT_WARN("Segment %s already loaded in exclusive mode!\n",
-			   dev_info->segment_name);
-		up_write(&dcssblk_devices_sem);
-		return count;
+		rc = -EBUSY;
+		goto out;
 	}
 	if (inbuf[0] == '1') {
 		// reload segment in shared mode
-		segment_unload(dev_info->segment_name);
-		rc = segment_load(dev_info->segment_name, SEGMENT_SHARED,
-					&dev_info->start, &dev_info->end);
+		rc = segment_modify_shared(dev_info->segment_name,
+					   SEGMENT_SHARED);
 		if (rc < 0) {
-			dcssblk_segment_warn(rc, dev_info->segment_name);
-			goto removeseg;
+			BUG_ON(rc == -EINVAL);
+			if (rc == -EIO || rc == -ENOENT)
+				goto removeseg;
+		} else {
+			dev_info->is_shared = 1;
+			switch (dev_info->segment_type) {
+				case SEG_TYPE_SR:
+				case SEG_TYPE_ER:
+				case SEG_TYPE_SC:
+					set_disk_ro(dev_info->gd,1);
+			}
 		}
-		dev_info->is_shared = 1;
-		if (rc == SEG_TYPE_SR || rc == SEG_TYPE_ER || rc == SEG_TYPE_SC)
-			set_disk_ro(dev_info->gd, 1);
 	} else if (inbuf[0] == '0') {
 		// reload segment in exclusive mode
 		if (dev_info->segment_type == SEG_TYPE_SC) {
 			PRINT_ERR("Segment type SC (%s) cannot be loaded in "
 				  "non-shared mode\n", dev_info->segment_name);
-			up_write(&dcssblk_devices_sem);
-			return -EINVAL;
+			rc = -EINVAL;
+			goto out;
 		}
-		segment_unload(dev_info->segment_name);
-		rc = segment_load(dev_info->segment_name, SEGMENT_EXCLUSIVE,
-					&dev_info->start, &dev_info->end);
+		rc = segment_modify_shared(dev_info->segment_name,
+					   SEGMENT_EXCLUSIVE);
 		if (rc < 0) {
-			dcssblk_segment_warn(rc, dev_info->segment_name);
-			goto removeseg;
+			BUG_ON(rc == -EINVAL);
+			if (rc == -EIO || rc == -ENOENT)
+				goto removeseg;
+		} else {
+			dev_info->is_shared = 0;
+			set_disk_ro(dev_info->gd, 0);
 		}
-		dev_info->is_shared = 0;
-		set_disk_ro(dev_info->gd, 0);
 	} else {
-		up_write(&dcssblk_devices_sem);
 		PRINT_WARN("Invalid value, must be 0 or 1\n");
-		return -EINVAL;
+		rc = -EINVAL;
+		goto out;
 	}
 	rc = count;
-	up_write(&dcssblk_devices_sem);
 	goto out;
 
 removeseg:
@@ -278,8 +275,8 @@
 	put_disk(dev_info->gd);
 	device_unregister(dev);
 	put_device(dev);
-	up_write(&dcssblk_devices_sem);
 out:
+	up_write(&dcssblk_devices_sem);
 	return rc;
 }
 
diff -urN linux-2.6/drivers/s390/char/monreader.c linux-2.6-patched/drivers/s390/char/monreader.c
--- linux-2.6/drivers/s390/char/monreader.c	2004-11-30 14:03:04.000000000 +0100
+++ linux-2.6-patched/drivers/s390/char/monreader.c	2004-11-30 14:03:19.000000000 +0100
@@ -116,7 +116,7 @@
 }
 
 /*
- * print appropriate error message for segment_load()/segment_info()
+ * print appropriate error message for segment_load()/segment_type()
  * return code
  */
 static void
@@ -155,6 +155,10 @@
 		P_WARNING("cannot load/query segment %s, out of memory\n",
 			  seg_name);
 		break;
+	case -ERANGE:
+		P_WARNING("cannot load/query segment %s, exceeds kernel "
+			  "mapping range\n", seg_name);
+		break;
 	default:
 		P_WARNING("cannot load/query segment %s, return value %i\n",
 			  seg_name, rc);
@@ -581,7 +585,7 @@
 		return -ENODEV;
 	}
 
-	rc = segment_info(mon_dcss_name);
+	rc = segment_type(mon_dcss_name);
 	if (rc < 0) {
 		mon_segment_warn(rc, mon_dcss_name);
 		return rc;
diff -urN linux-2.6/include/asm-s390/extmem.h linux-2.6-patched/include/asm-s390/extmem.h
--- linux-2.6/include/asm-s390/extmem.h	2004-11-30 14:03:08.000000000 +0100
+++ linux-2.6-patched/include/asm-s390/extmem.h	2004-11-30 14:03:19.000000000 +0100
@@ -17,6 +17,7 @@
 #define SEG_TYPE_SN 4
 #define SEG_TYPE_EN 5
 #define SEG_TYPE_SC 6
+#define SEG_TYPE_EWEN 7
 
 #define SEGMENT_SHARED 0
 #define SEGMENT_EXCLUSIVE 1
@@ -24,7 +25,8 @@
 extern int segment_load (char *name,int segtype,unsigned long *addr,unsigned long *length);
 extern void segment_unload(char *name);
 extern void segment_save(char *name);
-extern int segment_info (char* name);
+extern int segment_type (char* name);
+extern int segment_modify_shared (char *name, int do_nonshared);
 
 #endif
 #endif

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2004-11-30 15:14 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-11-30 15:09 [patch 3/6] s390: dcss segments Martin Schwidefsky

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome