diff --git a/lib/Sys/Syscall.pm b/lib/Sys/Syscall.pm new file mode 100644 index 0000000..0a87d44 --- /dev/null +++ b/lib/Sys/Syscall.pm @@ -0,0 +1,150 @@ +package Sys::Syscall; +use strict; +use POSIX (); + +our $loaded_syscall = 0; + +sub _load_syscall { + return if $loaded_syscall++; + my $clean = sub { + delete @INC{qw}; + }; + $clean->(); # don't trust modules before us + my $rv = eval { require 'syscall.ph'; 1 } || eval { require 'sys/syscall.ph'; 1 }; + $clean->(); # don't require modules after us trust us + return $rv; +} + +our ($sysname, $nodename, $release, $version, $machine) = POSIX::uname(); + +our $epoll_defined = 0; # set if epoll is recognized + +our ( + $SYS_epoll_create, + $SYS_epoll_ctl, + $SYS_epoll_wait, + $SYS_sendfile + ); + +if ($^O eq "linux") { + # whether the machine requires 64-bit numbers to be on 8-byte + # boundaries. + my $u64_mod_8 = 0; + $epoll_defined = 1; + + if ($machine =~ m/^i[3456]86$/) { + $SYS_epoll_create = 254; + $SYS_epoll_ctl = 255; + $SYS_epoll_wait = 256; + $SYS_sendfile = 187; # or 64: 239 + } elsif ($machine eq "x86_64") { + $SYS_epoll_create = 213; + $SYS_epoll_ctl = 233; + $SYS_epoll_wait = 232; + $SYS_sendfile = 187; # or 64: 239 + } elsif ($machine eq "ppc64") { + $SYS_epoll_create = 236; + $SYS_epoll_ctl = 237; + $SYS_epoll_wait = 238; + $SYS_sendfile = 186; # (sys32_sendfile). sys32_sendfile64=226 (64 bit processes: sys_sendfile64=186) + $u64_mod_8 = 1; + } elsif ($machine eq "ppc") { + $SYS_epoll_create = 236; + $SYS_epoll_ctl = 237; + $SYS_epoll_wait = 238; + $SYS_sendfile = 186; # sys_sendfile64=226 + $u64_mod_8 = 1; + } elsif ($machine eq "ia64") { + $SYS_epoll_create = 1243; + $SYS_epoll_ctl = 1244; + $SYS_epoll_wait = 1245; + $SYS_sendfile = 1187; + $u64_mod_8 = 1; + } + + if ($u64_mod_8) { + *epoll_wait = \&epoll_wait_mod8; + *epoll_ctl = \&epoll_ctl_mod8; + } else { + *epoll_wait = \&epoll_wait_mod4; + *epoll_ctl = \&epoll_ctl_mod4; + } +} + +unless ($SYS_sendfile) { + _load_syscall(); + $SYS_sendfile = eval { &SYS_sendfile; } || 0; +} + +*sendfile = $SYS_sendfile ? \&_sendfile_wrapper : \&_sendfile_noimpl; + +sub _sendfile_noimpl { + die "sendfile syscall number not found. Run h2ph?"; +} +sub _sendfile_wrapper { + return syscall( + $SYS_sendfile, + $_[0] + 0, # fd + $_[1] + 0, # fd + $_[2] + 0, # offset (NULL means kernel moves offset) + $_[3] + 0 # count + ); +} + +# epoll_create wrapper +# ARGS: (size) -- but in modern Linux 2.6, the +# size doesn't even matter (radix tree now, not hash) +sub epoll_create { + return -1 unless defined $SYS_epoll_create; + my $epfd = eval { syscall($SYS_epoll_create, $_[0]+0 || 100) }; + return -1 if $@; + return $epfd; +} + +# epoll_ctl wrapper +# ARGS: (epfd, op, fd, events_mask) +sub epoll_ctl_mod4 { + syscall($SYS_epoll_ctl, $_[0]+0, $_[1]+0, $_[2]+0, pack("LLL", $_[3], $_[2], 0)); +} +sub epoll_ctl_mod8 { + syscall($SYS_epoll_ctl, $_[0]+0, $_[1]+0, $_[2]+0, pack("LLLL", $_[3], 0, $_[2], 0)); +} + +# epoll_wait wrapper +# ARGS: (epfd, maxevents, timeout (milliseconds), arrayref) +# arrayref: values modified to be [$fd, $event] +our $epoll_wait_events; +our $epoll_wait_size = 0; +sub epoll_wait_mod4 { + # resize our static buffer if requested size is bigger than we've ever done + if ($_[1] > $epoll_wait_size) { + $epoll_wait_size = $_[1]; + $epoll_wait_events = "\0" x 12 x $epoll_wait_size; + } + my $ct = syscall($SYS_epoll_wait, $_[0]+0, $epoll_wait_events, $_[1]+0, $_[2]+0); + for ($_ = 0; $_ < $ct; $_++) { + @{$_[3]->[$_]}[1,0] = unpack("LL", substr($epoll_wait_events, 12*$_, 8)); + } + return $ct; +} + +sub epoll_wait_mod8 { + # resize our static buffer if requested size is bigger than we've ever done + if ($_[1] > $epoll_wait_size) { + $epoll_wait_size = $_[1]; + $epoll_wait_events = "\0" x 16 x $epoll_wait_size; + } + my $ct = syscall($SYS_epoll_wait, $_[0]+0, $epoll_wait_events, $_[1]+0, $_[2]+0); + for ($_ = 0; $_ < $ct; $_++) { + # 16 byte epoll_event structs, with format: + # 4 byte mask [idx 1] + # 4 byte padding (we put it into idx 2, useless) + # 8 byte data (first 4 bytes are fd, into idx 0) + @{$_[3]->[$_]}[1,2,0] = unpack("LLL", substr($epoll_wait_events, 16*$_, 12)); + } + return $ct; +} + + +1;