From: Martin Schwidefsky 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 * _