s390/sclp: avoid merged message output

The sclp console and tty code currently uses several message text
objects in a single message event to print several lines with one
SCCB. This causes the output of these lines to be fused into a
block which is noticeable when selecting text in the operating system
message panel.

Instead use several message events with a single message text object
each to print every line on its own. This changes the SCCB layout
from

    struct sccb_header
        struct evbuf_header
            struct mdb_header
                struct go
                struct mto
		...
		struct mto

to

    struct sccb_header
        struct evbuf_header
            struct mdb_header
                struct go
                struct mto
	...
        struct evbuf_header
            struct mdb_header
                struct go
                struct mto

Reviewed-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index 35a84af..6010cd3 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -47,9 +47,9 @@
 sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
 {
 	struct sclp_buffer *buffer;
-	struct write_sccb *sccb;
+	struct sccb_header *sccb;
 
-	sccb = (struct write_sccb *) page;
+	sccb = (struct sccb_header *) page;
 	/*
 	 * We keep the struct sclp_buffer structure at the end
 	 * of the sccb page.
@@ -57,24 +57,16 @@
 	buffer = ((struct sclp_buffer *) ((addr_t) sccb + PAGE_SIZE)) - 1;
 	buffer->sccb = sccb;
 	buffer->retry_count = 0;
-	buffer->mto_number = 0;
-	buffer->mto_char_sum = 0;
+	buffer->messages = 0;
+	buffer->char_sum = 0;
 	buffer->current_line = NULL;
 	buffer->current_length = 0;
 	buffer->columns = columns;
 	buffer->htab = htab;
 
 	/* initialize sccb */
-	memset(sccb, 0, sizeof(struct write_sccb));
-	sccb->header.length = sizeof(struct write_sccb);
-	sccb->msg_buf.header.length = sizeof(struct msg_buf);
-	sccb->msg_buf.header.type = EVTYP_MSG;
-	sccb->msg_buf.mdb.header.length = sizeof(struct mdb);
-	sccb->msg_buf.mdb.header.type = 1;
-	sccb->msg_buf.mdb.header.tag = 0xD4C4C240;	/* ebcdic "MDB " */
-	sccb->msg_buf.mdb.header.revision_code = 1;
-	sccb->msg_buf.mdb.go.length = sizeof(struct go);
-	sccb->msg_buf.mdb.go.type = 1;
+	memset(sccb, 0, sizeof(struct sccb_header));
+	sccb->length = sizeof(struct sccb_header);
 
 	return buffer;
 }
@@ -90,37 +82,49 @@
 }
 
 /*
- * Initialize a new Message Text Object (MTO) at the end of the provided buffer
- * with enough room for max_len characters. Return 0 on success.
+ * Initialize a new message the end of the provided buffer with
+ * enough room for max_len characters. Return 0 on success.
  */
 static int
 sclp_initialize_mto(struct sclp_buffer *buffer, int max_len)
 {
-	struct write_sccb *sccb;
+	struct sccb_header *sccb;
+	struct msg_buf *msg;
+	struct mdb *mdb;
+	struct go *go;
 	struct mto *mto;
-	int mto_size;
+	int msg_size;
 
-	/* max size of new Message Text Object including message text  */
-	mto_size = sizeof(struct mto) + max_len;
+	/* max size of new message including message text  */
+	msg_size = sizeof(struct msg_buf) + max_len;
 
 	/* check if current buffer sccb can contain the mto */
 	sccb = buffer->sccb;
-	if ((MAX_SCCB_ROOM - sccb->header.length) < mto_size)
+	if ((MAX_SCCB_ROOM - sccb->length) < msg_size)
 		return -ENOMEM;
 
-	/* find address of new message text object */
-	mto = (struct mto *)(((addr_t) sccb) + sccb->header.length);
+	msg = (struct msg_buf *)((addr_t) sccb + sccb->length);
+	memset(msg, 0, sizeof(struct msg_buf));
+	msg->header.length = sizeof(struct msg_buf);
+	msg->header.type = EVTYP_MSG;
 
-	/*
-	 * fill the new Message-Text Object,
-	 * starting behind the former last byte of the SCCB
-	 */
-	memset(mto, 0, sizeof(struct mto));
+	mdb = &msg->mdb;
+	mdb->header.length = sizeof(struct mdb);
+	mdb->header.type = 1;
+	mdb->header.tag = 0xD4C4C240;	/* ebcdic "MDB " */
+	mdb->header.revision_code = 1;
+
+	go = &mdb->go;
+	go->length = sizeof(struct go);
+	go->type = 1;
+
+	mto = &mdb->mto;
 	mto->length = sizeof(struct mto);
 	mto->type = 4;	/* message text object */
 	mto->line_type_flags = LNTPFLGS_ENDTEXT; /* end text */
 
 	/* set pointer to first byte after struct mto. */
+	buffer->current_msg = msg;
 	buffer->current_line = (char *) (mto + 1);
 	buffer->current_length = 0;
 
@@ -128,45 +132,37 @@
 }
 
 /*
- * Finalize MTO initialized by sclp_initialize_mto(), updating the sizes of
- * MTO, enclosing MDB, event buffer and SCCB.
+ * Finalize message initialized by sclp_initialize_mto(),
+ * updating the sizes of MTO, enclosing MDB, event buffer and SCCB.
  */
 static void
 sclp_finalize_mto(struct sclp_buffer *buffer)
 {
-	struct write_sccb *sccb;
-	struct mto *mto;
-	int str_len, mto_size;
-
-	str_len = buffer->current_length;
-	buffer->current_line = NULL;
-	buffer->current_length = 0;
-
-	/* real size of new Message Text Object including message text	*/
-	mto_size = sizeof(struct mto) + str_len;
-
-	/* find address of new message text object */
-	sccb = buffer->sccb;
-	mto = (struct mto *)(((addr_t) sccb) + sccb->header.length);
-
-	/* set size of message text object */
-	mto->length = mto_size;
+	struct sccb_header *sccb;
+	struct msg_buf *msg;
 
 	/*
 	 * update values of sizes
 	 * (SCCB, Event(Message) Buffer, Message Data Block)
 	 */
-	sccb->header.length += mto_size;
-	sccb->msg_buf.header.length += mto_size;
-	sccb->msg_buf.mdb.header.length += mto_size;
+	sccb = buffer->sccb;
+	msg = buffer->current_msg;
+	msg->header.length += buffer->current_length;
+	msg->mdb.header.length += buffer->current_length;
+	msg->mdb.mto.length += buffer->current_length;
+	sccb->length += msg->header.length;
 
 	/*
 	 * count number of buffered messages (= number of Message Text
 	 * Objects) and number of buffered characters
 	 * for the SCCB currently used for buffering and at all
 	 */
-	buffer->mto_number++;
-	buffer->mto_char_sum += str_len;
+	buffer->messages++;
+	buffer->char_sum += buffer->current_length;
+
+	buffer->current_line = NULL;
+	buffer->current_length = 0;
+	buffer->current_msg = NULL;
 }
 
 /*
@@ -218,7 +214,13 @@
 			break;
 		case '\a':	/* bell, one for several times	*/
 			/* set SCLP sound alarm bit in General Object */
-			buffer->sccb->msg_buf.mdb.go.general_msg_flags |=
+			if (buffer->current_line == NULL) {
+				rc = sclp_initialize_mto(buffer,
+							 buffer->columns);
+				if (rc)
+					return i_msg;
+			}
+			buffer->current_msg->mdb.go.general_msg_flags |=
 				GNRLMSGFLGS_SNDALRM;
 			break;
 		case '\t':	/* horizontal tabulator	 */
@@ -309,11 +311,13 @@
 int
 sclp_buffer_space(struct sclp_buffer *buffer)
 {
+	struct sccb_header *sccb;
 	int count;
 
-	count = MAX_SCCB_ROOM - buffer->sccb->header.length;
+	sccb = buffer->sccb;
+	count = MAX_SCCB_ROOM - sccb->length;
 	if (buffer->current_line != NULL)
-		count -= sizeof(struct mto) + buffer->current_length;
+		count -= sizeof(struct msg_buf) + buffer->current_length;
 	return count;
 }
 
@@ -325,7 +329,7 @@
 {
 	int count;
 
-	count = buffer->mto_char_sum;
+	count = buffer->char_sum;
 	if (buffer->current_line != NULL)
 		count += buffer->current_length;
 	return count;
@@ -378,7 +382,7 @@
 {
 	int rc;
 	struct sclp_buffer *buffer;
-	struct write_sccb *sccb;
+	struct sccb_header *sccb;
 
 	buffer = (struct sclp_buffer *) data;
 	sccb = buffer->sccb;
@@ -389,7 +393,7 @@
 		return;
 	}
 	/* check SCLP response code and choose suitable action	*/
-	switch (sccb->header.response_code) {
+	switch (sccb->response_code) {
 	case 0x0020 :
 		/* Normal completion, buffer processed, message(s) sent */
 		rc = 0;
@@ -403,7 +407,7 @@
 		/* remove processed buffers and requeue rest */
 		if (sclp_remove_processed((struct sccb_header *) sccb) > 0) {
 			/* not all buffers were processed */
-			sccb->header.response_code = 0x0000;
+			sccb->response_code = 0x0000;
 			buffer->request.status = SCLP_REQ_FILLED;
 			rc = sclp_add_request(request);
 			if (rc == 0)
@@ -419,14 +423,14 @@
 			break;
 		}
 		/* retry request */
-		sccb->header.response_code = 0x0000;
+		sccb->response_code = 0x0000;
 		buffer->request.status = SCLP_REQ_FILLED;
 		rc = sclp_add_request(request);
 		if (rc == 0)
 			return;
 		break;
 	default:
-		if (sccb->header.response_code == 0x71f0)
+		if (sccb->response_code == 0x71f0)
 			rc = -ENOMEM;
 		else
 			rc = -EINVAL;
@@ -445,25 +449,19 @@
 sclp_emit_buffer(struct sclp_buffer *buffer,
 		 void (*callback)(struct sclp_buffer *, int))
 {
-	struct write_sccb *sccb;
-
 	/* add current line if there is one */
 	if (buffer->current_line != NULL)
 		sclp_finalize_mto(buffer);
 
 	/* Are there messages in the output buffer ? */
-	if (buffer->mto_number == 0)
+	if (buffer->messages == 0)
 		return -EIO;
 
-	sccb = buffer->sccb;
-	/* Use normal write message */
-	sccb->msg_buf.header.type = EVTYP_MSG;
-
 	buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA;
 	buffer->request.status = SCLP_REQ_FILLED;
 	buffer->request.callback = sclp_writedata_callback;
 	buffer->request.callback_data = buffer;
-	buffer->request.sccb = sccb;
+	buffer->request.sccb = buffer->sccb;
 	buffer->callback = callback;
 	return sclp_add_request(&buffer->request);
 }
diff --git a/drivers/s390/char/sclp_rw.h b/drivers/s390/char/sclp_rw.h
index 7a7bfc9..e3b0290 100644
--- a/drivers/s390/char/sclp_rw.h
+++ b/drivers/s390/char/sclp_rw.h
@@ -45,6 +45,7 @@
 struct mdb {
 	struct mdb_header header;
 	struct go go;
+	struct mto mto;
 } __attribute__((packed));
 
 struct msg_buf {
@@ -52,14 +53,9 @@
 	struct mdb mdb;
 } __attribute__((packed));
 
-struct write_sccb {
-	struct sccb_header header;
-	struct msg_buf msg_buf;
-} __attribute__((packed));
-
 /* The number of empty mto buffers that can be contained in a single sccb. */
-#define NR_EMPTY_MTO_PER_SCCB ((PAGE_SIZE - sizeof(struct sclp_buffer) - \
-			sizeof(struct write_sccb)) / sizeof(struct mto))
+#define NR_EMPTY_MSG_PER_SCCB ((PAGE_SIZE - sizeof(struct sclp_buffer) - \
+			sizeof(struct sccb_header)) / sizeof(struct msg_buf))
 
 /*
  * data structure for information about list of SCCBs (only for writing),
@@ -68,7 +64,8 @@
 struct sclp_buffer {
 	struct list_head list;		/* list_head for sccb_info chain */
 	struct sclp_req request;
-	struct write_sccb *sccb;
+	void *sccb;
+	struct msg_buf *current_msg;
 	char *current_line;
 	int current_length;
 	int retry_count;
@@ -76,8 +73,8 @@
 	unsigned short columns;
 	unsigned short htab;
 	/* statistics about this buffer */
-	unsigned int mto_char_sum;	/* # chars in sccb */
-	unsigned int mto_number;	/* # mtos in sccb */
+	unsigned int char_sum;		/* # chars in sccb */
+	unsigned int messages;		/* # messages in sccb */
 	/* Callback that is called after reaching final status. */
 	void (*callback)(struct sclp_buffer *, int);
 };
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index 0036632..3c6e174 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -84,8 +84,8 @@
  * to change as output buffers get emptied, or if the output flow
  * control is acted. This is not an exact number because not every
  * character needs the same space in the sccb. The worst case is
- * a string of newlines. Every newlines creates a new mto which
- * needs 8 bytes.
+ * a string of newlines. Every newline creates a new message which
+ * needs 82 bytes.
  */
 static int
 sclp_tty_write_room (struct tty_struct *tty)
@@ -97,9 +97,9 @@
 	spin_lock_irqsave(&sclp_tty_lock, flags);
 	count = 0;
 	if (sclp_ttybuf != NULL)
-		count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct mto);
+		count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct msg_buf);
 	list_for_each(l, &sclp_tty_pages)
-		count += NR_EMPTY_MTO_PER_SCCB;
+		count += NR_EMPTY_MSG_PER_SCCB;
 	spin_unlock_irqrestore(&sclp_tty_lock, flags);
 	return count;
 }