| From 5a21a10b5f11e50237ab5a264deb0d5e55ba90fb Mon Sep 17 00:00:00 2001 |
| From: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> |
| Date: Tue, 24 Aug 2010 16:05:48 +0000 |
| Subject: tcp: select(writefds) don't hang up when a peer close connection |
| |
| |
| From: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> |
| |
| [ Upstream commit d84ba638e4ba3c40023ff997aa5e8d3ed002af36 ] |
| |
| This issue come from ruby language community. Below test program |
| hang up when only run on Linux. |
| |
| % uname -mrsv |
| Linux 2.6.26-2-486 #1 Sat Dec 26 08:37:39 UTC 2009 i686 |
| % ruby -rsocket -ve ' |
| BasicSocket.do_not_reverse_lookup = true |
| serv = TCPServer.open("127.0.0.1", 0) |
| s1 = TCPSocket.open("127.0.0.1", serv.addr[1]) |
| s2 = serv.accept |
| s2.close |
| s1.write("a") rescue p $! |
| s1.write("a") rescue p $! |
| Thread.new { |
| s1.write("a") |
| }.join' |
| ruby 1.9.3dev (2010-07-06 trunk 28554) [i686-linux] |
| #<Errno::EPIPE: Broken pipe> |
| [Hang Here] |
| |
| FreeBSD, Solaris, Mac doesn't. because Ruby's write() method call |
| select() internally. and tcp_poll has a bug. |
| |
| SUS defined 'ready for writing' of select() as following. |
| |
| | A descriptor shall be considered ready for writing when a call to an output |
| | function with O_NONBLOCK clear would not block, whether or not the function |
| | would transfer data successfully. |
| |
| That said, EPIPE situation is clearly one of 'ready for writing'. |
| |
| We don't have read-side issue because tcp_poll() already has read side |
| shutdown care. |
| |
| | if (sk->sk_shutdown & RCV_SHUTDOWN) |
| | mask |= POLLIN | POLLRDNORM | POLLRDHUP; |
| |
| So, Let's insert same logic in write side. |
| |
| - reference url |
| http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/31065 |
| http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/31068 |
| |
| Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| --- |
| net/ipv4/tcp.c | 3 ++- |
| 1 file changed, 2 insertions(+), 1 deletion(-) |
| |
| --- a/net/ipv4/tcp.c |
| +++ b/net/ipv4/tcp.c |
| @@ -451,7 +451,8 @@ unsigned int tcp_poll(struct file *file, |
| if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) |
| mask |= POLLOUT | POLLWRNORM; |
| } |
| - } |
| + } else |
| + mask |= POLLOUT | POLLWRNORM; |
| |
| if (tp->urg_data & TCP_URG_VALID) |
| mask |= POLLPRI; |