From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id D518E1F4B7 for ; Mon, 24 Jun 2019 02:59:24 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 52/57] daemon: set TCP_DEFER_ACCEPT on everything but NNTP Date: Mon, 24 Jun 2019 02:52:53 +0000 Message-Id: <20190624025258.25592-53-e@80x24.org> In-Reply-To: <20190624025258.25592-1-e@80x24.org> References: <20190624025258.25592-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: This Linux-specific option can save us some wakeups during the TLS negotiation phase, and it can help with ordinary HTTP, too. Plain NNTP (and in the future, POP3) are the only things which require the server send messages, first. --- lib/PublicInbox/Daemon.pm | 26 ++++++++++++++++++++++---- t/httpd-corner.t | 19 +++++++++++++++++++ t/httpd.t | 8 ++++++++ t/nntpd-tls.t | 11 ++++++++++- 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/lib/PublicInbox/Daemon.pm b/lib/PublicInbox/Daemon.pm index c4481555..8b59b65f 100644 --- a/lib/PublicInbox/Daemon.pm +++ b/lib/PublicInbox/Daemon.pm @@ -8,6 +8,7 @@ use warnings; use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/; use IO::Handle; use IO::Socket; +use Socket qw(IPPROTO_TCP); use Cwd qw/abs_path/; STDOUT->autoflush(1); STDERR->autoflush(1); @@ -552,6 +553,18 @@ sub tls_start_cb ($$) { } } +sub defer_accept ($) { + if ($^O eq 'linux') { + my ($s) = @_; + my $x = getsockopt($s, IPPROTO_TCP, Socket::TCP_DEFER_ACCEPT()); + return unless defined $x; # may be Unix socket + my $sec = unpack('i', $x); + return if $sec > 0; # systemd users may set a higher value + setsockopt($s, IPPROTO_TCP, Socket::TCP_DEFER_ACCEPT(), 1); + } + # TODO FreeBSD accf_http / accf_data +} + sub daemon_loop ($$$) { my ($refresh, $post_accept, $nntpd) = @_; PublicInbox::EvCleanup::enable(); # early for $refresh @@ -581,10 +594,15 @@ sub daemon_loop ($$$) { $SIG{HUP} = $refresh; $SIG{CHLD} = 'DEFAULT'; $SIG{$_} = 'IGNORE' for qw(USR2 TTIN TTOU WINCH); - # this calls epoll_create: - @listeners = map { - PublicInbox::Listener->new($_, - $post_accept{sockname($_)} || $post_accept) + @listeners = map {; + my $tls_cb = $post_accept{sockname($_)}; + + # NNTPS, HTTPS, HTTP, and POP3S are client-first traffic + # NNTP and POP3 are server-first + defer_accept($_) if $tls_cb || !$nntpd; + + # this calls epoll_create: + PublicInbox::Listener->new($_, $tls_cb || $post_accept) } @listeners; PublicInbox::DS->EventLoop; $parent_pipe = undef; diff --git a/t/httpd-corner.t b/t/httpd-corner.t index c1dc77db..13befcf1 100644 --- a/t/httpd-corner.t +++ b/t/httpd-corner.t @@ -36,6 +36,17 @@ my %opts = ( Listen => 1024, ); my $sock = IO::Socket::INET->new(%opts); +my $defer_accept_val; +if ($^O eq 'linux') { + setsockopt($sock, IPPROTO_TCP, Socket::TCP_DEFER_ACCEPT(), 5) or die; + my $x = getsockopt($sock, IPPROTO_TCP, Socket::TCP_DEFER_ACCEPT()); + defined $x or die "getsockopt: $!"; + $defer_accept_val = unpack('i', $x); + if ($defer_accept_val <= 0) { + die "unexpected TCP_DEFER_ACCEPT value: $defer_accept_val"; + } +} + my $upath = "$tmpdir/s"; my $unix = IO::Socket::UNIX->new( Listen => 1024, @@ -497,6 +508,14 @@ SKIP: { is($body, sha1_hex(''), 'read expected body #2'); } +SKIP: { + skip 'TCP_DEFER_ACCEPT is Linux-only', 1 if $^O ne 'linux'; + my $var = Socket::TCP_DEFER_ACCEPT(); + defined(my $x = getsockopt($sock, IPPROTO_TCP, $var)) or die; + is(unpack('i', $x), $defer_accept_val, + 'TCP_DEFER_ACCEPT unchanged if previously set'); +}; + done_testing(); sub capture { diff --git a/t/httpd.t b/t/httpd.t index c061031c..8c2a3173 100644 --- a/t/httpd.t +++ b/t/httpd.t @@ -10,6 +10,7 @@ foreach my $mod (qw(Plack::Util Plack::Builder HTTP::Date HTTP::Status)) { } use File::Temp qw/tempdir/; use IO::Socket::INET; +use Socket qw(IPPROTO_TCP); require './t/common.perl'; # FIXME: too much setup @@ -99,6 +100,13 @@ EOF 'fsck on cloned directory successful'); } +SKIP: { + skip 'TCP_DEFER_ACCEPT is Linux-only', 1 if $^O ne 'linux'; + my $var = Socket::TCP_DEFER_ACCEPT(); + defined(my $x = getsockopt($sock, IPPROTO_TCP, $var)) or die; + ok(unpack('i', $x) > 0, 'TCP_DEFER_ACCEPT set'); +}; + done_testing(); 1; diff --git a/t/nntpd-tls.t b/t/nntpd-tls.t index e8fb63b4..ef683cab 100644 --- a/t/nntpd-tls.t +++ b/t/nntpd-tls.t @@ -4,7 +4,7 @@ use strict; use warnings; use Test::More; use File::Temp qw(tempdir); -use Socket qw(SOCK_STREAM); +use Socket qw(SOCK_STREAM IPPROTO_TCP); # IO::Poll and Net::NNTP are part of the standard library, but # distros may split them off... foreach my $mod (qw(DBD::SQLite IO::Socket::SSL Net::NNTP IO::Poll)) { @@ -182,6 +182,15 @@ for my $args ( is(sysread($slow, my $eof, 4096), 0, 'got EOF'); $slow = undef; + SKIP: { + skip 'TCP_DEFER_ACCEPT is Linux-only', 2 if $^O ne 'linux'; + my $var = Socket::TCP_DEFER_ACCEPT(); + defined(my $x = getsockopt($nntps, IPPROTO_TCP, $var)) or die; + ok(unpack('i', $x) > 0, 'TCP_DEFER_ACCEPT set on NNTPS'); + defined($x = getsockopt($starttls, IPPROTO_TCP, $var)) or die; + is(unpack('i', $x), 0, 'TCP_DEFER_ACCEPT is 0 on plain NNTP'); + }; + $c = undef; kill('TERM', $pid); is($pid, waitpid($pid, 0), 'nntpd exited successfully'); -- EW