From patchwork Thu Jan 21 09:00:15 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Kroah-Hartman X-Patchwork-Id: 368402 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 14CA5C433E0 for ; Thu, 21 Jan 2021 09:01:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C4F01239D0 for ; Thu, 21 Jan 2021 09:01:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726668AbhAUJBZ (ORCPT ); Thu, 21 Jan 2021 04:01:25 -0500 Received: from mail.kernel.org ([198.145.29.99]:36584 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728340AbhAUJBO (ORCPT ); Thu, 21 Jan 2021 04:01:14 -0500 Received: by mail.kernel.org (Postfix) with ESMTPSA id C776722473; Thu, 21 Jan 2021 09:00:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1611219631; bh=K7tHFWmO+SytEv9K8H586UnBlSIX0eYSPupIZD2/lq8=; h=From:To:Cc:Subject:Date:From; b=1j7+qNnNzQTcRAq7KRiWv060duDdC7S1RHZDks/qVz0sgJtat7+aN85GQGoUHbYbJ K8B2GwXc3JPw/mW64/f0p63X/6IymaL7iWqVnFrPOt/GmxyqGwL5KcQTmD+0vG/80k QF1mxmbjfE/2o3zW+qm+T/4cMusQszOUmingrTxc= From: Greg Kroah-Hartman To: jirislaby@kernel.org, linux-serial@vger.kernel.org Cc: gregkh@linuxfoundation.org, hch@lst.de, viro@zeniv.linux.org.uk, linux-kernel@vger.kernel.org, ohw.giles@gmail.com, r.karszniewicz@phytec.de, Linus Torvalds Subject: [PATCH 1/6] tty: implement write_iter Date: Thu, 21 Jan 2021 10:00:15 +0100 Message-Id: <20210121090020.3147058-1-gregkh@linuxfoundation.org> X-Mailer: git-send-email 2.30.0 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-serial@vger.kernel.org From: Linus Torvalds This makes the tty layer use the .write_iter() function instead of the traditional .write() functionality. That allows writev(), but more importantly also makes it possible to enable .splice_write() for ttys, reinstating the "splice to tty" functionality that was lost in commit 36e2c7421f02 ("fs: don't allow splice read/write without explicit ops"). Fixes: 36e2c7421f02 ("fs: don't allow splice read/write without explicit ops") Reported-by: Oliver Giles Cc: Christoph Hellwig Cc: Greg Kroah-Hartman Cc: Al Viro Signed-off-by: Linus Torvalds Reported-by: Jiri Slaby Signed-off-by: Linus Torvalds Reviewed-by: Jiri Slaby --- drivers/tty/tty_io.c | 48 ++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 56ade99ef99f..338bc4ef5549 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -143,9 +143,8 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */ DEFINE_MUTEX(tty_mutex); static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); -static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *); -ssize_t redirected_tty_write(struct file *, const char __user *, - size_t, loff_t *); +static ssize_t tty_write(struct kiocb *, struct iov_iter *); +ssize_t redirected_tty_write(struct kiocb *, struct iov_iter *); static __poll_t tty_poll(struct file *, poll_table *); static int tty_open(struct inode *, struct file *); long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg); @@ -478,7 +477,8 @@ static void tty_show_fdinfo(struct seq_file *m, struct file *file) static const struct file_operations tty_fops = { .llseek = no_llseek, .read = tty_read, - .write = tty_write, + .write_iter = tty_write, + .splice_write = iter_file_splice_write, .poll = tty_poll, .unlocked_ioctl = tty_ioctl, .compat_ioctl = tty_compat_ioctl, @@ -491,7 +491,8 @@ static const struct file_operations tty_fops = { static const struct file_operations console_fops = { .llseek = no_llseek, .read = tty_read, - .write = redirected_tty_write, + .write_iter = redirected_tty_write, + .splice_write = iter_file_splice_write, .poll = tty_poll, .unlocked_ioctl = tty_ioctl, .compat_ioctl = tty_compat_ioctl, @@ -607,9 +608,9 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) /* This breaks for file handles being sent over AF_UNIX sockets ? */ list_for_each_entry(priv, &tty->tty_files, list) { filp = priv->file; - if (filp->f_op->write == redirected_tty_write) + if (filp->f_op->write_iter == redirected_tty_write) cons_filp = filp; - if (filp->f_op->write != tty_write) + if (filp->f_op->write_iter != tty_write) continue; closecount++; __tty_fasync(-1, filp, 0); /* can't block */ @@ -902,9 +903,9 @@ static inline ssize_t do_tty_write( ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t), struct tty_struct *tty, struct file *file, - const char __user *buf, - size_t count) + struct iov_iter *from) { + size_t count = iov_iter_count(from); ssize_t ret, written = 0; unsigned int chunk; @@ -956,14 +957,20 @@ static inline ssize_t do_tty_write( size_t size = count; if (size > chunk) size = chunk; + ret = -EFAULT; - if (copy_from_user(tty->write_buf, buf, size)) + if (copy_from_iter(tty->write_buf, size, from) != size) break; + ret = write(tty, file, tty->write_buf, size); if (ret <= 0) break; + + /* FIXME! Have Al check this! */ + if (ret != size) + iov_iter_revert(from, size-ret); + written += ret; - buf += ret; count -= ret; if (!count) break; @@ -1023,9 +1030,9 @@ void tty_write_message(struct tty_struct *tty, char *msg) * write method will not be invoked in parallel for each device. */ -static ssize_t tty_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from) { + struct file *file = iocb->ki_filp; struct tty_struct *tty = file_tty(file); struct tty_ldisc *ld; ssize_t ret; @@ -1038,18 +1045,15 @@ static ssize_t tty_write(struct file *file, const char __user *buf, if (tty->ops->write_room == NULL) tty_err(tty, "missing write_room method\n"); ld = tty_ldisc_ref_wait(tty); - if (!ld) - return hung_up_tty_write(file, buf, count, ppos); - if (!ld->ops->write) + if (!ld || !ld->ops->write) ret = -EIO; else - ret = do_tty_write(ld->ops->write, tty, file, buf, count); + ret = do_tty_write(ld->ops->write, tty, file, from); tty_ldisc_deref(ld); return ret; } -ssize_t redirected_tty_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +ssize_t redirected_tty_write(struct kiocb *iocb, struct iov_iter *iter) { struct file *p = NULL; @@ -1060,11 +1064,11 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf, if (p) { ssize_t res; - res = vfs_write(p, buf, count, &p->f_pos); + res = vfs_iocb_iter_write(p, iocb, iter); fput(p); return res; } - return tty_write(file, buf, count, ppos); + return tty_write(iocb, iter); } /** @@ -2293,7 +2297,7 @@ static int tioccons(struct file *file) { if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (file->f_op->write == redirected_tty_write) { + if (file->f_op->write_iter == redirected_tty_write) { struct file *f; spin_lock(&redirect_lock); f = redirect; From patchwork Thu Jan 21 09:00:18 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Kroah-Hartman X-Patchwork-Id: 368401 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1EA6FC433DB for ; Thu, 21 Jan 2021 09:02:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DD13F23602 for ; Thu, 21 Jan 2021 09:02:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728128AbhAUJCB (ORCPT ); Thu, 21 Jan 2021 04:02:01 -0500 Received: from mail.kernel.org ([198.145.29.99]:36746 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728192AbhAUJBZ (ORCPT ); Thu, 21 Jan 2021 04:01:25 -0500 Received: by mail.kernel.org (Postfix) with ESMTPSA id 69F77239D3; Thu, 21 Jan 2021 09:00:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1611219645; bh=dDUjS4EvPptksdmDNHTIiYldi+hap7CTKjBhDBkBOBI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uHwKLx8EAIcecMW0pB/c8M6GSsD+ML3kjEBjGKPC2yTzspsaFvJt0mAmykDB7na5U HYhsqY5coFslO7tqi+kbZmFxUGlktmaZt54inMatU3zeqPGGy1e4jB7jizTx5R0NbL QPyWtNHdpk+nwg6TzzwxpqInQSqEwqsf0FbBS/hE= From: Greg Kroah-Hartman To: jirislaby@kernel.org, linux-serial@vger.kernel.org Cc: gregkh@linuxfoundation.org, hch@lst.de, viro@zeniv.linux.org.uk, linux-kernel@vger.kernel.org, ohw.giles@gmail.com, r.karszniewicz@phytec.de, Linus Torvalds Subject: [PATCH 4/6] tty: clean up legacy leftovers from n_tty line discipline Date: Thu, 21 Jan 2021 10:00:18 +0100 Message-Id: <20210121090020.3147058-4-gregkh@linuxfoundation.org> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210121090020.3147058-1-gregkh@linuxfoundation.org> References: <20210121090020.3147058-1-gregkh@linuxfoundation.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-serial@vger.kernel.org From: Linus Torvalds Back when the line disciplines did their own direct user accesses, they had to deal with the data copy possibly failing in the middle. Now that the user copy is done by the tty_io.c code, that failure case no longer exists. Remove the left-over error handling code that cannot trigger. Signed-off-by: Linus Torvalds --- drivers/tty/n_tty.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 4a34a9f43b29..3a1a79462d16 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1955,19 +1955,17 @@ static inline int input_available_p(struct tty_struct *tty, int poll) * read_tail published */ -static int copy_from_read_buf(struct tty_struct *tty, +static void copy_from_read_buf(struct tty_struct *tty, unsigned char **kbp, size_t *nr) { struct n_tty_data *ldata = tty->disc_data; - int retval; size_t n; bool is_eof; size_t head = smp_load_acquire(&ldata->commit_head); size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1); - retval = 0; n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail); n = min(*nr, n); if (n) { @@ -1984,7 +1982,6 @@ static int copy_from_read_buf(struct tty_struct *tty, *kbp += n; *nr -= n; } - return retval; } /** @@ -2010,9 +2007,9 @@ static int copy_from_read_buf(struct tty_struct *tty, * read_tail published */ -static int canon_copy_from_read_buf(struct tty_struct *tty, - unsigned char **kbp, - size_t *nr) +static void canon_copy_from_read_buf(struct tty_struct *tty, + unsigned char **kbp, + size_t *nr) { struct n_tty_data *ldata = tty->disc_data; size_t n, size, more, c; @@ -2022,7 +2019,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, /* N.B. avoid overrun if nr == 0 */ if (!*nr) - return 0; + return; n = min(*nr + 1, smp_load_acquire(&ldata->canon_head) - ldata->read_tail); @@ -2069,7 +2066,6 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, ldata->push = 0; tty_audit_push(); } - return 0; } extern ssize_t redirected_tty_write(struct file *, const char __user *, @@ -2222,24 +2218,17 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, } if (ldata->icanon && !L_EXTPROC(tty)) { - retval = canon_copy_from_read_buf(tty, &kb, &nr); - if (retval) - break; + canon_copy_from_read_buf(tty, &kb, &nr); } else { - int uncopied; - /* Deal with packet mode. */ if (packet && kb == kbuf) { *kb++ = TIOCPKT_DATA; nr--; } - uncopied = copy_from_read_buf(tty, &kb, &nr); - uncopied += copy_from_read_buf(tty, &kb, &nr); - if (uncopied) { - retval = -EFAULT; - break; - } + /* See comment above copy_from_read_buf() why twice */ + copy_from_read_buf(tty, &kb, &nr); + copy_from_read_buf(tty, &kb, &nr); } n_tty_check_unthrottle(tty); From patchwork Thu Jan 21 09:00:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Kroah-Hartman X-Patchwork-Id: 368400 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id DFD62C433E0 for ; Thu, 21 Jan 2021 09:42:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 94A952399A for ; Thu, 21 Jan 2021 09:42:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728728AbhAUJlo (ORCPT ); Thu, 21 Jan 2021 04:41:44 -0500 Received: from mail.kernel.org ([198.145.29.99]:36800 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728357AbhAUJBa (ORCPT ); Thu, 21 Jan 2021 04:01:30 -0500 Received: by mail.kernel.org (Postfix) with ESMTPSA id E709D239D4; Thu, 21 Jan 2021 09:00:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1611219647; bh=ZQ3Z7a02ovbA4sCg+DtDcdngxJVq2M1jXjk+wzP7ddQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nF8g6t7ggJhh01a2RfdVM/F9LEL5SUEU/++0TZwMsHCANcj7cQl4qTuqGvNF9eHwu coeLMyzy69QxaPqETu4b4JOU3vCxNjcN4nUZuBFb6UIOX0jDkuluKoSz/3/wsu1aXX DPmeEQJScTP2eKWW8fJrYBXdH3z0qd6h48luTVIw= From: Greg Kroah-Hartman To: jirislaby@kernel.org, linux-serial@vger.kernel.org Cc: gregkh@linuxfoundation.org, hch@lst.de, viro@zeniv.linux.org.uk, linux-kernel@vger.kernel.org, ohw.giles@gmail.com, r.karszniewicz@phytec.de, Linus Torvalds Subject: [PATCH 5/6] tty: teach n_tty line discipline about the new "cookie continuations" Date: Thu, 21 Jan 2021 10:00:19 +0100 Message-Id: <20210121090020.3147058-5-gregkh@linuxfoundation.org> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210121090020.3147058-1-gregkh@linuxfoundation.org> References: <20210121090020.3147058-1-gregkh@linuxfoundation.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-serial@vger.kernel.org From: Linus Torvalds With the conversion to do the tty ldisc read operations in small chunks, the n_tty line discipline became noticeably slower for throughput oriented loads, because rather than read things in up to 2kB chunks, it would return at most 64 bytes per read() system call. The cost is mainly all in the "do system calls over and over", not really in the new "copy to an extra kernel buffer". This can be fixed by teaching the n_tty line discipline about the "cookie continuation" model, which the chunking code supports because things like hdlc need to be able to handle packets up to 64kB in size. Doing that doesn't just get us back to the old performace, but to much better performance: my stupid "copy 10MB of data over a pty" test program is now almost twice as fast as it used to be (going down from 0.1s to 0.054s). This is entirely because it now creates maximal chunks (which happens to be "one byte less than one page" due to how we do the circular tty buffers). NOTE! This case only handles the simpler non-icanon case, which is the one where people may care about throughput. I'm going to do the icanon case later too, because while performance isn't a major issue for that, there may be programs that think they'll always get a full line and don't like the 64-byte chunking for that reason. Such programs are arguably buggy (signals etc can cause random partial results from tty reads anyway), and good programs will handle such partial reads, but expecting everybody to write "good programs" has never been a winning policy for the kernel.. Signed-off-by: Linus Torvalds --- drivers/tty/n_tty.c | 52 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 3a1a79462d16..b89308d52ade 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1943,19 +1943,17 @@ static inline int input_available_p(struct tty_struct *tty, int poll) * Helper function to speed up n_tty_read. It is only called when * ICANON is off; it copies characters straight from the tty queue. * - * It can be profitably called twice; once to drain the space from - * the tail pointer to the (physical) end of the buffer, and once - * to drain the space from the (physical) beginning of the buffer - * to head pointer. - * * Called under the ldata->atomic_read_lock sem * + * Returns true if it successfully copied data, but there is still + * more data to be had. + * * n_tty_read()/consumer path: * caller holds non-exclusive termios_rwsem * read_tail published */ -static void copy_from_read_buf(struct tty_struct *tty, +static bool copy_from_read_buf(struct tty_struct *tty, unsigned char **kbp, size_t *nr) @@ -1978,10 +1976,14 @@ static void copy_from_read_buf(struct tty_struct *tty, /* Turn single EOF into zero-length read */ if (L_EXTPROC(tty) && ldata->icanon && is_eof && (head == ldata->read_tail)) - n = 0; + return false; *kbp += n; *nr -= n; + + /* If we have more to copy, let the caller know */ + return head != ldata->read_tail; } + return false; } /** @@ -2132,6 +2134,25 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, int packet; size_t tail; + /* + * Is this a continuation of a read started earler? + * + * If so, we still hold the atomic_read_lock and the + * termios_rwsem, and can just continue to copy data. + */ + if (*cookie) { + if (copy_from_read_buf(tty, &kb, &nr)) + return kb - kbuf; + + /* No more data - release locks and stop retries */ + n_tty_kick_worker(tty); + n_tty_check_unthrottle(tty); + up_read(&tty->termios_rwsem); + mutex_unlock(&ldata->atomic_read_lock); + *cookie = NULL; + return kb - kbuf; + } + c = job_control(tty, file); if (c < 0) return c; @@ -2226,9 +2247,20 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, nr--; } - /* See comment above copy_from_read_buf() why twice */ - copy_from_read_buf(tty, &kb, &nr); - copy_from_read_buf(tty, &kb, &nr); + /* + * Copy data, and if there is more to be had + * and we have nothing more to wait for, then + * let's mark us for retries. + * + * NOTE! We return here with both the termios_sem + * and atomic_read_lock still held, the retries + * will release them when done. + */ + if (copy_from_read_buf(tty, &kb, &nr) && kb - kbuf >= minimum) { + remove_wait_queue(&tty->read_wait, &wait); + *cookie = cookie; + return kb - kbuf; + } } n_tty_check_unthrottle(tty);