X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FSpawn.pm;h=1ee40503c33f0b014bb6814295d890e3335133e2;hb=af0b0fb7a454470a32c452119d0392e0dedb3fe1;hp=db679b770cfd81d985c4416db9b7186d718587a4;hpb=2d0438df824317049e29837c6d187b54ea89b515;p=public-inbox.git diff --git a/lib/PublicInbox/Spawn.pm b/lib/PublicInbox/Spawn.pm index db679b77..1ee40503 100644 --- a/lib/PublicInbox/Spawn.pm +++ b/lib/PublicInbox/Spawn.pm @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2020 all contributors +# Copyright (C) 2016-2021 all contributors # License: AGPL-3.0+ # # This allows vfork to be used for spawning subprocesses if @@ -10,13 +10,16 @@ # daemons (inside the PSGI code (-httpd) and -nntpd). The short-lived # scripts (-mda, -index, -learn, -init) either use IPC::run or standard # Perl routines. +# +# There'll probably be more OS-level C stuff here, down the line. +# We don't want too many DSOs: https://udrepper.livejournal.com/8790.html package PublicInbox::Spawn; use strict; use parent qw(Exporter); use Symbol qw(gensym); use PublicInbox::ProcessPipe; -our @EXPORT_OK = qw/which spawn popen_rd/; +our @EXPORT_OK = qw(which spawn popen_rd run_die nodatacow_dir); our @RLIMITS = qw(RLIMIT_CPU RLIMIT_CORE RLIMIT_DATA); my $vfork_spawn = <<'VFORK_SPAWN'; @@ -25,6 +28,7 @@ my $vfork_spawn = <<'VFORK_SPAWN'; #include #include #include +#include /* some platforms need alloca.h, but some don't */ #if defined(__GNUC__) && !defined(alloca) @@ -144,12 +148,65 @@ int pi_fork_exec(SV *redirref, SV *file, SV *cmdref, SV *envref, SV *rlimref, } VFORK_SPAWN +# btrfs on Linux is copy-on-write (COW) by default. As of Linux 5.7, +# this still leads to fragmentation for SQLite and Xapian files where +# random I/O happens, so we disable COW just for SQLite files and Xapian +# directories. Disabling COW disables checksumming, so we only do this +# for regeneratable files, and not canonical git storage (git doesn't +# checksum refs, only data under $GIT_DIR/objects). +my $set_nodatacow = $^O eq 'linux' ? <<'SET_NODATACOW' : ''; +#include +#include +#include +#include +#include +#include +#include +#include + +void nodatacow_fd(int fd) +{ + struct statfs buf; + int val = 0; + + if (fstatfs(fd, &buf) < 0) { + fprintf(stderr, "fstatfs: %s\\n", strerror(errno)); + return; + } + + /* only btrfs is known to have this problem, so skip for non-btrfs */ + if (buf.f_type != BTRFS_SUPER_MAGIC) + return; + + if (ioctl(fd, FS_IOC_GETFLAGS, &val) < 0) { + fprintf(stderr, "FS_IOC_GET_FLAGS: %s\\n", strerror(errno)); + return; + } + val |= FS_NOCOW_FL; + if (ioctl(fd, FS_IOC_SETFLAGS, &val) < 0) + fprintf(stderr, "FS_IOC_SET_FLAGS: %s\\n", strerror(errno)); +} + +void nodatacow_dir(const char *dir) +{ + DIR *dh = opendir(dir); + int fd; + + if (!dh) croak("opendir(%s): %s", dir, strerror(errno)); + fd = dirfd(dh); + if (fd >= 0) + nodatacow_fd(fd); + /* ENOTSUP probably won't happen under Linux... */ + closedir(dh); +} +SET_NODATACOW + my $inline_dir = $ENV{PERL_INLINE_DIRECTORY} //= ( $ENV{XDG_CACHE_HOME} // ( ($ENV{HOME} // '/nonexistent').'/.cache' ) ).'/public-inbox/inline-c'; -$vfork_spawn = undef unless -d $inline_dir && -w _; +$set_nodatacow = $vfork_spawn = undef unless -d $inline_dir && -w _; if (defined $vfork_spawn) { # Inline 0.64 or later has locking in multi-process env, # but we support 0.5 on Debian wheezy @@ -158,14 +215,21 @@ if (defined $vfork_spawn) { my $f = "$inline_dir/.public-inbox.lock"; open my $fh, '>', $f or die "failed to open $f: $!\n"; flock($fh, LOCK_EX) or die "LOCK_EX failed on $f: $!\n"; - eval 'use Inline C => $vfork_spawn'; + eval 'use Inline C => $vfork_spawn . $set_nodatacow'; my $err = $@; + my $ndc_err; + if ($err && $set_nodatacow) { # missing Linux kernel headers + $ndc_err = $err; + undef $set_nodatacow; + eval 'use Inline C => $vfork_spawn'; + } flock($fh, LOCK_UN) or die "LOCK_UN failed on $f: $!\n"; die $err if $err; + warn $ndc_err if $ndc_err; }; if ($@) { warn "Inline::C failed for vfork: $@\n"; - $vfork_spawn = undef; + $set_nodatacow = $vfork_spawn = undef; } } @@ -173,6 +237,14 @@ unless (defined $vfork_spawn) { require PublicInbox::SpawnPP; *pi_fork_exec = \&PublicInbox::SpawnPP::pi_fork_exec } +unless ($set_nodatacow) { + require PublicInbox::NDC_PP; + no warnings 'once'; + *nodatacow_fd = \&PublicInbox::NDC_PP::nodatacow_fd; + *nodatacow_dir = \&PublicInbox::NDC_PP::nodatacow_dir; +} +undef $set_nodatacow; +undef $vfork_spawn; sub which ($) { my ($file) = @_; @@ -218,7 +290,7 @@ sub spawn ($;$$) { } my $cd = $opts->{'-C'} // ''; # undef => NULL mapping doesn't work? my $pid = pi_fork_exec($redir, $f, $cmd, \@env, $rlim, $cd); - die "fork_exec failed: $!\n" unless $pid > 0; + die "fork_exec @$cmd failed: $!\n" unless $pid > 0; $pid; } @@ -234,4 +306,11 @@ sub popen_rd { $ret; } +sub run_die ($;$$) { + my ($cmd, $env, $rdr) = @_; + my $pid = spawn($cmd, $env, $rdr); + waitpid($pid, 0) == $pid or die "@$cmd did not finish"; + $? == 0 or die "@$cmd failed: \$?=$?\n"; +} + 1;