From: Martin Schwidefsky <schwidefsky@de.ibm.com>

Console driver fixes:
 - Fix ^n at end-of-line check in 3215 and sclp driver.
 - Fix copy_from_user/might_sleep/spinlock problem in sclp.


---

 25-akpm/drivers/s390/char/con3215.c    |    2 
 25-akpm/drivers/s390/char/sclp_tty.c   |   15 +++---
 25-akpm/drivers/s390/char/sclp_vt220.c |   80 ++++++++++++++++++++-------------
 3 files changed, 58 insertions(+), 39 deletions(-)

diff -puN drivers/s390/char/con3215.c~s390-03-console-driver drivers/s390/char/con3215.c
--- 25/drivers/s390/char/con3215.c~s390-03-console-driver	Fri Feb 20 15:56:59 2004
+++ 25-akpm/drivers/s390/char/con3215.c	Fri Feb 20 15:56:59 2004
@@ -454,7 +454,7 @@ raw3215_irq(struct ccw_device *cdev, uns
 				memcpy(tty->flip.char_buf_ptr,
 				       raw->inbuf, count);
 				if (count < 2 ||
-				    (strncmp(raw->inbuf+count-2, "^n", 2) ||
+				    (strncmp(raw->inbuf+count-2, "^n", 2) &&
 				    strncmp(raw->inbuf+count-2, "\252n", 2)) ) {
 					/* don't add the auto \n */
 					tty->flip.char_buf_ptr[count] = '\n';
diff -puN drivers/s390/char/sclp_tty.c~s390-03-console-driver drivers/s390/char/sclp_tty.c
--- 25/drivers/s390/char/sclp_tty.c~s390-03-console-driver	Fri Feb 20 15:56:59 2004
+++ 25-akpm/drivers/s390/char/sclp_tty.c	Fri Feb 20 15:56:59 2004
@@ -529,8 +529,8 @@ sclp_tty_input(unsigned char* buf, unsig
 		/* send (normal) input to line discipline */
 		memcpy(sclp_tty->flip.char_buf_ptr, buf, count);
 		if (count < 2 ||
-		    strncmp ((const char *) buf + count - 2, "^n", 2) ||
-		    strncmp ((const char *) buf + count - 2, "\0252n", 2)) {
+		    (strncmp ((const char *) buf + count - 2, "^n", 2) &&
+		     strncmp ((const char *) buf + count - 2, "\0252n", 2))) {
 			sclp_tty->flip.char_buf_ptr[count] = '\n';
 			count++;
 		} else
@@ -636,7 +636,7 @@ find_gds_vector(struct gds_vector *start
 {
 	struct gds_vector *vec;
 
-	for (vec = start; vec < end; (void *) vec += vec->length)
+	for (vec = start; vec < end; vec = (void *) vec + vec->length)
 		if (vec->gds_id == id)
 			return vec;
 	return NULL;
@@ -648,7 +648,8 @@ find_gds_subvector(struct gds_subvector 
 {
 	struct gds_subvector *subvec;
 
-	for (subvec = start; subvec < end; (void *) subvec += subvec->length)
+	for (subvec = start; subvec < end;
+	     subvec = (void *) subvec + subvec->length)
 		if (subvec->key == key)
 			return subvec;
 	return NULL;
@@ -667,7 +668,7 @@ sclp_eval_selfdeftextmsg(struct gds_subv
 			break;
 		sclp_get_input((unsigned char *)(subvec + 1),
 			       (unsigned char *) subvec + subvec->length);
-		(void *) subvec += subvec->length;
+		subvec = (void *) subvec + subvec->length;
 	}
 }
 
@@ -685,7 +686,7 @@ sclp_eval_textcmd(struct gds_subvector *
 			break;
 		sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1),
 					 (void *)subvec + subvec->length);
-		(void *) subvec += subvec->length;
+		subvec = (void *) subvec + subvec->length;
 	}
 }
 
@@ -701,7 +702,7 @@ sclp_eval_cpmsu(struct gds_vector *start
 			break;
 		sclp_eval_textcmd((struct gds_subvector *)(vec + 1),
 				  (void *) vec + vec->length);
-		(void *) vec += vec->length;
+		vec = (void *) vec + vec->length;
 	}
 }
 
diff -puN drivers/s390/char/sclp_vt220.c~s390-03-console-driver drivers/s390/char/sclp_vt220.c
--- 25/drivers/s390/char/sclp_vt220.c~s390-03-console-driver	Fri Feb 20 15:56:59 2004
+++ 25-akpm/drivers/s390/char/sclp_vt220.c	Fri Feb 20 15:56:59 2004
@@ -32,9 +32,10 @@
 #define SCLP_VT220_MAJOR		TTY_MAJOR
 #define SCLP_VT220_MINOR		65
 #define SCLP_VT220_DRIVER_NAME		"sclp_vt220"
-#define SCLP_VT220_DEVICE_NAME		"sclp_vt"
+#define SCLP_VT220_DEVICE_NAME		"ttysclp"
 #define SCLP_VT220_CONSOLE_NAME		"ttyS"
 #define SCLP_VT220_CONSOLE_INDEX	1	/* console=ttyS1 */
+#define SCLP_VT220_BUF_SIZE		80
 
 /* Representation of a single write request */
 struct sclp_vt220_request {
@@ -336,12 +337,11 @@ sclp_vt220_chars_stored(struct sclp_vt22
 
 /*
  * Add msg to buffer associated with request. Return the number of characters
- * added or -EFAULT on error.
+ * added.
  */
 static int
 sclp_vt220_add_msg(struct sclp_vt220_request *request,
-		   const unsigned char *msg, int count, int from_user,
-		   int convertlf)
+		   const unsigned char *msg, int count, int convertlf)
 {
 	struct sclp_vt220_sccb *sccb;
 	void *buffer;
@@ -363,11 +363,7 @@ sclp_vt220_add_msg(struct sclp_vt220_req
 		     (from < count) && (to < sclp_vt220_space_left(request));
 		     from++) {
 			/* Retrieve character */
-			if (from_user) {
-				if (get_user(c, msg + from) != 0)
-					return -EFAULT;
-			} else
-				c = msg[from];
+			c = msg[from];
 			/* Perform conversion */
 			if (c == 0x0a) {
 				if (to + 1 < sclp_vt220_space_left(request)) {
@@ -383,12 +379,7 @@ sclp_vt220_add_msg(struct sclp_vt220_req
 		sccb->evbuf.length += to;
 		return from;
 	} else {
-		if (from_user) {
-			if (copy_from_user(buffer, (void *) msg, count) != 0)
-				return -EFAULT;
-		}
-		else
-			memcpy(buffer, (const void *) msg, count);
+		memcpy(buffer, (const void *) msg, count);
 		sccb->header.length += count;
 		sccb->evbuf.length += count;
 		return count;
@@ -408,7 +399,7 @@ sclp_vt220_timeout(unsigned long data)
 
 /* 
  * Internal implementation of the write function. Write COUNT bytes of data
- * from memory at BUF which may reside in user space (specified by FROM_USER)
+ * from memory at BUF
  * to the SCLP interface. In case that the data does not fit into the current
  * write buffer, emit the current one and allocate a new one. If there are no
  * more empty buffers available, wait until one gets emptied. If DO_SCHEDULE
@@ -419,8 +410,8 @@ sclp_vt220_timeout(unsigned long data)
  * of bytes written.
  */
 static int
-__sclp_vt220_write(int from_user, const unsigned char *buf, int count,
-		   int do_schedule, int convertlf)
+__sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,
+		   int convertlf)
 {
 	unsigned long flags;
 	void *page;
@@ -451,10 +442,9 @@ __sclp_vt220_write(int from_user, const 
 		}
 		/* Try to write the string to the current request buffer */
 		written = sclp_vt220_add_msg(sclp_vt220_current_request,
-				buf, count, from_user, convertlf);
-		if (written > 0)
-			overall_written += written;
-		if (written == -EFAULT || written == count)
+					     buf, count, convertlf);
+		overall_written += written;
+		if (written == count)
 			break;
 		/*
 		 * Not all characters could be written to the current
@@ -489,7 +479,29 @@ static int
 sclp_vt220_write(struct tty_struct *tty, int from_user,
 		 const unsigned char *buf, int count)
 {
-	return __sclp_vt220_write(from_user, buf, count, 1, 0);
+	int length;
+	int ret;
+
+	if (!from_user)
+		return __sclp_vt220_write(buf, count, 1, 0);
+	/* Use intermediate buffer to prevent calling copy_from_user() while
+	 * holding a lock. */
+	ret = 0;
+	while (count > 0) {
+		length = count < SCLP_VT220_BUF_SIZE ?
+			 count : SCLP_VT220_BUF_SIZE;
+		length -= copy_from_user(tty->driver_data, buf, length);
+		if (length == 0) {
+			if (!ret)
+				return -EFAULT;
+			break;
+		}
+		length = __sclp_vt220_write(tty->driver_data, length, 1, 0);
+		buf += length;
+		count -= length;
+		ret += length;
+	}
+	return ret;
 }
 
 #define SCLP_VT220_SESSION_ENDED	0x01
@@ -541,9 +553,13 @@ sclp_vt220_receiver_fn(struct evbuf_head
 static int
 sclp_vt220_open(struct tty_struct *tty, struct file *filp)
 {
-	sclp_vt220_tty = tty;
-	tty->driver_data = NULL;
-	tty->low_latency = 0;
+	if (tty->count == 1) {
+		sclp_vt220_tty = tty;
+		tty->driver_data = kmalloc(SCLP_VT220_BUF_SIZE, GFP_KERNEL);
+		if (tty->driver_data == NULL)
+			return -ENOMEM;
+		tty->low_latency = 0;
+	}
 	return 0;
 }
 
@@ -553,9 +569,11 @@ sclp_vt220_open(struct tty_struct *tty, 
 static void
 sclp_vt220_close(struct tty_struct *tty, struct file *filp)
 {
-	if (tty->count > 1)
-		return;
-	sclp_vt220_tty = NULL;
+	if (tty->count == 1) {
+		sclp_vt220_tty = NULL;
+		kfree(tty->driver_data);
+		tty->driver_data = NULL;
+	}
 }
 
 /*
@@ -571,7 +589,7 @@ sclp_vt220_close(struct tty_struct *tty,
 static void
 sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch)
 {
-	__sclp_vt220_write(0, &ch, 1, 0, 0);
+	__sclp_vt220_write(&ch, 1, 0, 0);
 }
 
 /*
@@ -765,7 +783,7 @@ module_init(sclp_vt220_tty_init);
 static void
 sclp_vt220_con_write(struct console *con, const char *buf, unsigned int count)
 {
-	__sclp_vt220_write(0, (const unsigned char *) buf, count, 1, 1);
+	__sclp_vt220_write((const unsigned char *) buf, count, 1, 1);
 }
 
 static struct tty_driver *

_