diff mbox series

[1/6] tty: implement write_iter

Message ID 20210121090020.3147058-1-gregkh@linuxfoundation.org
State New
Headers show
Series [1/6] tty: implement write_iter | expand

Commit Message

Greg KH Jan. 21, 2021, 9 a.m. UTC
From: Linus Torvalds <torvalds@linux-foundation.org>

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 <ohw.giles@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
 drivers/tty/tty_io.c | 48 ++++++++++++++++++++++++--------------------
 1 file changed, 26 insertions(+), 22 deletions(-)

Comments

Jiri Slaby Jan. 21, 2021, 9:39 a.m. UTC | #1
On 21. 01. 21, 10:00, Greg Kroah-Hartman wrote:
> From: Linus Torvalds <torvalds@linux-foundation.org>
> 
> 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 <ohw.giles@gmail.com>
> Cc: Christoph Hellwig <hch@lst.de>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Al Viro <viro@zeniv.linux.org.uk>
> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
> ---
>   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)

This now relies on implicit value of hung_up_tty_fops.write_iter (i.e. 
NULL), okay.

>   			continue;
>   		closecount++;
>   		__tty_fasync(-1, filp, 0);	/* can't block */
 > 		filp->f_op = &hung_up_tty_fops;

Isn't this racy with VFS layer in vfs_write:
         if (file->f_op->write)
                 ret = file->f_op->write(file, buf, count, pos);
         else if (file->f_op->write_iter)
                 ret = new_sync_write(file, buf, count, pos);

? hung_up_tty_fops do not set iter_write and tty_fops do not set write. 
When we switch from one to the other here, right after the 'if', but 
before the call, what happens? Likely nothing for the ->write case 
immediately as compilers cache the value, but for ->write_iter, I'm not 
sure. Anyway, this looks broken to me. (Read on.)

> @@ -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);

Ok, here belongs my earlier note: "if ld == NULL => crash here." That is 
if hangup happens during the ldisc wait, the kernel will crash in 
tty_ldisc_deref.

Is there a reason not to convert hung_up_tty_fops too and leave the 
return hung_up_tty_write here intact? This would also solve the comments 
above.

>   	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;
>   

thanks,
Jiri Slaby Jan. 21, 2021, 10:44 a.m. UTC | #2
On 21. 01. 21, 10:00, Greg Kroah-Hartman wrote:
> From: Linus Torvalds <torvalds@linux-foundation.org>
> 
> Now that the ldisc read() function takes kernel pointers, it's fairly
> straightforward to make the tty file operations use .read_iter() instead
> of .read().
> 
> That automatically gives us vread() and friends, and also makes it
> possible to do .splice_read() on ttys again.
> 
> Fixes: 36e2c7421f02 ("fs: don't allow splice read/write without explicit ops")
> Reported-by: Oliver Giles <ohw.giles@gmail.com>
> Cc: Christoph Hellwig <hch@lst.de>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Al Viro <viro@zeniv.linux.org.uk>
> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
> ---
>   drivers/tty/tty_io.c | 36 ++++++++++++++++++------------------
>   1 file changed, 18 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
> index a34f8bcf875e..8846d3b99845 100644
> --- a/drivers/tty/tty_io.c
> +++ b/drivers/tty/tty_io.c
...
> @@ -907,10 +909,10 @@ static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, struct
>    *	read calls may be outstanding in parallel.
>    */
>   
> -static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
> -			loff_t *ppos)
> +static ssize_t tty_read(struct kiocb *iocb, struct iov_iter *to)
>   {
>   	int i;
> +	struct file *file = iocb->ki_filp;
>   	struct inode *inode = file_inode(file);
>   	struct tty_struct *tty = file_tty(file);
>   	struct tty_ldisc *ld;
> @@ -923,11 +925,9 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
>   	/* We want to wait for the line discipline to sort out in this
>   	   situation */
>   	ld = tty_ldisc_ref_wait(tty);
> -	if (!ld)
> -		return hung_up_tty_read(file, buf, count, ppos);
>   	i = -EIO;
> -	if (ld->ops->read)
> -		i = iterate_tty_read(ld, tty, file, buf, count);
> +	if (ld && ld->ops->read)
> +		i = iterate_tty_read(ld, tty, file, to);
>   	tty_ldisc_deref(ld);

Here we have the same problem as in tty_write.

And also the other one with hung_up_tty_read not converted.

thanks,
Linus Torvalds Jan. 21, 2021, 5:44 p.m. UTC | #3
On Thu, Jan 21, 2021 at 1:40 AM Jiri Slaby <jirislaby@kernel.org> wrote:
>
> Ok, here belongs my earlier note: "if ld == NULL => crash here." That is
> if hangup happens during the ldisc wait, the kernel will crash in
> tty_ldisc_deref.

Right you are, good catch.

> Is there a reason not to convert hung_up_tty_fops too and leave the
> return hung_up_tty_write here intact? This would also solve the comments
> above.

No, no reason. I started out just changing that one tty_write, then
noticed that I had to change the redirect case too, but never then got
to "yeah, I should have changed the hup case as well".

Greg, do you prefer a new series, or incremental patches?

               Linus
Greg KH Jan. 21, 2021, 5:57 p.m. UTC | #4
On Thu, Jan 21, 2021 at 09:44:17AM -0800, Linus Torvalds wrote:
> On Thu, Jan 21, 2021 at 1:40 AM Jiri Slaby <jirislaby@kernel.org> wrote:
> >
> > Ok, here belongs my earlier note: "if ld == NULL => crash here." That is
> > if hangup happens during the ldisc wait, the kernel will crash in
> > tty_ldisc_deref.
> 
> Right you are, good catch.
> 
> > Is there a reason not to convert hung_up_tty_fops too and leave the
> > return hung_up_tty_write here intact? This would also solve the comments
> > above.
> 
> No, no reason. I started out just changing that one tty_write, then
> noticed that I had to change the redirect case too, but never then got
> to "yeah, I should have changed the hup case as well".
> 
> Greg, do you prefer a new series, or incremental patches?

Incremental patches please as these are already in my public branches
and I would have to revert them and add new ones but that's messy, so
fixes on top is fine.

thanks,

greg k-h
Linus Torvalds Jan. 21, 2021, 6:42 p.m. UTC | #5
On Thu, Jan 21, 2021 at 9:57 AM Greg Kroah-Hartman
<gregkh@linuxfoundation.org> wrote:
>
> Incremental patches please as these are already in my public branches
> and I would have to revert them and add new ones but that's messy, so
> fixes on top is fine.

Ok. And since I think you put that first tty_write conversion patch in
a different branch from the tty_read one, I did the fixup patches for
the two as separate patches, even though they really just do the exact
same thing.

So here's three patches: the two fixups for the hung_up_tty case, and
the EOVERFLOW error case that Jiri also noted. I've also updated the
'tty-splice' branch if you prefer them that way.

And I *should* say that I still haven't tested _any_ of the HDLC
changes. I have no idea how to do that, and if somebody can point to a
test-case (or better yet, actually has a real life situation where
they use it and can test this all) it would be great.

Jiri, any other issues, or any comment of yours I missed? I didn't do
the min() thing, I find the explicit conditional more legible myself,
but won't complain if somebody else then disagrees and wants to clean
it up.

(On the matter of cleanups: when reading through the ICANON handling
in canon_copy_from_read_buf(), that code is really completely
incomprehensible. I know how it works, and why it does it, but I had
to remind myself, because the code just looks crazy and does things
like "*nr+1" to walk _past_ the point we actually copy etc. I was very
tempted to rewrite that entirely, but wanting to keep my changes
minimal and targeted made me not do so).

                Linus
From bf6ee858fdff2a1800fd198bbe90034dcd60f3ef Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@linux-foundation.org>
Date: Thu, 21 Jan 2021 10:04:27 -0800
Subject: [PATCH 1/3] tty: fix up hung_up_tty_write() conversion

In commit "tty: implement write_iter", I left the write_iter conversion
of the hung up tty case alone, because I incorrectly thought it didn't
matter.

Jiri showed me the errors of my ways, and pointed out the problems with
that incomplete conversion.  Fix it all up.

Reported-by: Jiri Slaby <jirislaby@kernel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
 drivers/tty/tty_io.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 8846d3b99845..52489f8b7401 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -437,8 +437,7 @@ static ssize_t hung_up_tty_read(struct file *file, char __user *buf,
 	return 0;
 }
 
-static ssize_t hung_up_tty_write(struct file *file, const char __user *buf,
-				 size_t count, loff_t *ppos)
+static ssize_t hung_up_tty_write(struct kiocb *iocb, struct iov_iter *from)
 {
 	return -EIO;
 }
@@ -506,7 +505,7 @@ static const struct file_operations console_fops = {
 static const struct file_operations hung_up_tty_fops = {
 	.llseek		= no_llseek,
 	.read		= hung_up_tty_read,
-	.write		= hung_up_tty_write,
+	.write_iter	= hung_up_tty_write,
 	.poll		= hung_up_tty_poll,
 	.unlocked_ioctl	= hung_up_tty_ioctl,
 	.compat_ioctl	= hung_up_tty_compat_ioctl,
@@ -1103,7 +1102,9 @@ static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from)
 	if (tty->ops->write_room == NULL)
 		tty_err(tty, "missing write_room method\n");
 	ld = tty_ldisc_ref_wait(tty);
-	if (!ld || !ld->ops->write)
+	if (!ld)
+		return hung_up_tty_write(iocb, from);
+	if (!ld->ops->write)
 		ret = -EIO;
 	else
 		ret = do_tty_write(ld->ops->write, tty, file, from);
Linus Torvalds Jan. 21, 2021, 9:09 p.m. UTC | #6
On Thu, Jan 21, 2021 at 11:43 AM Greg Kroah-Hartman
<gregkh@linuxfoundation.org> wrote:
>
> This works, thanks for these.  I'll wait for Jiri to review them before
> applying them to my branches...

Let's hope Jiri sees them, since he had some email issue earlier..

I'll add his suse address here too.

           Linus
Jiri Slaby Jan. 22, 2021, 7:07 a.m. UTC | #7
On 21. 01. 21, 22:09, Linus Torvalds wrote:
> On Thu, Jan 21, 2021 at 11:43 AM Greg Kroah-Hartman

> <gregkh@linuxfoundation.org> wrote:

>>

>> This works, thanks for these.  I'll wait for Jiri to review them before

>> applying them to my branches...

> 

> Let's hope Jiri sees them, since he had some email issue earlier..

> 

> I'll add his suse address here too.


Thanks, I am fixed and nothing was lost :).

-- 
js
suse labs
Jiri Slaby Jan. 22, 2021, 7:33 a.m. UTC | #8
On 21. 01. 21, 19:42, Linus Torvalds wrote:
> On Thu, Jan 21, 2021 at 9:57 AM Greg Kroah-Hartman

> <gregkh@linuxfoundation.org> wrote:

>>

>> Incremental patches please as these are already in my public branches

>> and I would have to revert them and add new ones but that's messy, so

>> fixes on top is fine.

> 

> Ok. And since I think you put that first tty_write conversion patch in

> a different branch from the tty_read one, I did the fixup patches for

> the two as separate patches, even though they really just do the exact

> same thing.

> 

> So here's three patches: the two fixups for the hung_up_tty case, and

> the EOVERFLOW error case that Jiri also noted. I've also updated the

> 'tty-splice' branch if you prefer them that way.

> 

> And I *should* say that I still haven't tested _any_ of the HDLC

> changes. I have no idea how to do that, and if somebody can point to a

> test-case (or better yet, actually has a real life situation where

> they use it and can test this all) it would be great.

> 

> Jiri, any other issues, or any comment of yours I missed? I didn't do

> the min() thing, I find the explicit conditional more legible myself,

> but won't complain if somebody else then disagrees and wants to clean

> it up.


I cannot find anything else.

All three:
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>


thanks,
-- 
js
Greg KH Jan. 22, 2021, 7:43 a.m. UTC | #9
On Fri, Jan 22, 2021 at 08:33:33AM +0100, Jiri Slaby wrote:
> On 21. 01. 21, 19:42, Linus Torvalds wrote:

> > On Thu, Jan 21, 2021 at 9:57 AM Greg Kroah-Hartman

> > <gregkh@linuxfoundation.org> wrote:

> > > 

> > > Incremental patches please as these are already in my public branches

> > > and I would have to revert them and add new ones but that's messy, so

> > > fixes on top is fine.

> > 

> > Ok. And since I think you put that first tty_write conversion patch in

> > a different branch from the tty_read one, I did the fixup patches for

> > the two as separate patches, even though they really just do the exact

> > same thing.

> > 

> > So here's three patches: the two fixups for the hung_up_tty case, and

> > the EOVERFLOW error case that Jiri also noted. I've also updated the

> > 'tty-splice' branch if you prefer them that way.

> > 

> > And I *should* say that I still haven't tested _any_ of the HDLC

> > changes. I have no idea how to do that, and if somebody can point to a

> > test-case (or better yet, actually has a real life situation where

> > they use it and can test this all) it would be great.

> > 

> > Jiri, any other issues, or any comment of yours I missed? I didn't do

> > the min() thing, I find the explicit conditional more legible myself,

> > but won't complain if somebody else then disagrees and wants to clean

> > it up.

> 

> I cannot find anything else.

> 

> All three:

> Reviewed-by: Jiri Slaby <jirislaby@kernel.org>


Thanks for the review, I'll go apply these in a bit...

greg k-h
diff mbox series

Patch

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;