X-Git-Url: https://git.proxmox.com/?p=pve-common.git;a=blobdiff_plain;f=src%2FPVE%2FProcFSTools.pm;h=7687c13c4e30b9d483ebb624c8d522ca8a74ca3d;hp=d170ef500c2a713d21e3562e19e1bf978815871b;hb=HEAD;hpb=f27d5e6b7a2a322e9e708ec7c93cca4df94a3a43 diff --git a/src/PVE/ProcFSTools.pm b/src/PVE/ProcFSTools.pm index d170ef5..3826fcc 100644 --- a/src/PVE/ProcFSTools.pm +++ b/src/PVE/ProcFSTools.pm @@ -2,13 +2,15 @@ package PVE::ProcFSTools; use strict; use warnings; + +use Cwd qw(); +use IO::File; +use List::Util qw(sum); use POSIX; +use Socket qw(PF_INET PF_INET6 SOCK_DGRAM IPPROTO_IP); use Time::HiRes qw (gettimeofday); -use IO::File; -use PVE::Tools; -use Cwd qw(); -use Socket qw(PF_INET PF_INET6 SOCK_DGRAM IPPROTO_IP); +use PVE::Tools; use constant IFF_UP => 1; use constant IFNAMSIZ => 16; @@ -35,6 +37,7 @@ sub read_cpuinfo { my $fh = IO::File->new ($fn, "r"); return $res if !$fh; + my $cpuid = 0; my $idhash = {}; my $count = 0; while (defined(my $line = <$fh>)) { @@ -47,7 +50,10 @@ sub read_cpuinfo { } elsif ($line =~ m/^flags\s*:\s*(.*)$/) { $res->{flags} = $1 if !length $res->{flags}; } elsif ($line =~ m/^physical id\s*:\s*(\d+)\s*$/i) { - $idhash->{$1} = 1; + $cpuid = $1; + $idhash->{$1} = 1 if not defined($idhash->{$1}); + } elsif ($line =~ m/^cpu cores\s*:\s*(\d+)\s*$/i) { + $idhash->{$cpuid} = $1 if defined($idhash->{$cpuid}); } } @@ -56,6 +62,8 @@ sub read_cpuinfo { $res->{sockets} = scalar(keys %$idhash) || 1; + $res->{cores} = sum(values %$idhash) || 1; + $res->{cpus} = $count; $fh->close; @@ -97,6 +105,23 @@ sub kernel_version { return (0, 0, 0, '', ''); } +# Check if the kernel is at least $major.$minor. Return either just a boolean, +# or a boolean and the kernel version's major.minor string from /proc/version +sub check_kernel_release { + my ($major, $minor) = @_; + + my ($k_major, $k_minor) = kernel_version(); + + my $ok; + if (defined($minor)) { + $ok = $k_major > $major || ($k_major == $major && $k_minor >= $minor); + } else { + $ok = $k_major >= $major; + } + + return wantarray ? ($ok, "$k_major.$k_minor") : $ok; +} + sub read_loadavg { my $line = PVE::Tools::file_read_firstline('/proc/loadavg'); @@ -108,22 +133,54 @@ sub read_loadavg { return wantarray ? (0, 0, 0) : 0; } +sub parse_pressure { + my ($path) = @_; + + my $res = {}; + my $v = qr/\d+\.\d+/; + my $fh = IO::File->new($path, "r") or return undef; + while (defined (my $line = <$fh>)) { + if ($line =~ /^(some|full)\s+avg10\=($v)\s+avg60\=($v)\s+avg300\=($v)\s+total\=(\d+)/) { + $res->{$1}->{avg10} = $2; + $res->{$1}->{avg60} = $3; + $res->{$1}->{avg300} = $4; + $res->{$1}->{total} = $4; + } + } + $fh->close; + return $res; +} + +sub read_pressure { + my $res = {}; + foreach my $type (qw(cpu memory io)) { + my $stats = parse_pressure("/proc/pressure/$type"); + $res->{$type} = $stats if $stats; + } + return $res; +} + my $last_proc_stat; sub read_proc_stat { - my $res = { user => 0, nice => 0, system => 0, idle => 0 , sum => 0}; + my $res = { user => 0, nice => 0, system => 0, idle => 0 , iowait => 0, irq => 0, softirq => 0, steal => 0, guest => 0, guest_nice => 0, sum => 0}; my $cpucount = 0; if (my $fh = IO::File->new ("/proc/stat", "r")) { while (defined (my $line = <$fh>)) { - if ($line =~ m|^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s|) { - $res->{user} = $1; - $res->{nice} = $2; + if ($line =~ m|^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)(?:\s+(\d+)\s+(\d+))?|) { + $res->{user} = $1 - ($9 // 0); + $res->{nice} = $2 - ($10 // 0); $res->{system} = $3; $res->{idle} = $4; - $res->{used} = $1+$2+$3; + $res->{used} = $1+$2+$3+$6+$7+$8; $res->{iowait} = $5; + $res->{irq} = $6; + $res->{softirq} = $7; + $res->{steal} = $8; + $res->{guest} = $9 // 0; + $res->{guest_nice} = $10 // 0; } elsif ($line =~ m|^cpu\d+\s|) { $cpucount++; } @@ -135,6 +192,18 @@ sub read_proc_stat { my $ctime = gettimeofday; # floating point time in seconds + # the sum of all fields + $res->{total} = $res->{user} + + $res->{nice} + + $res->{system} + + $res->{iowait} + + $res->{irq} + + $res->{softirq} + + $res->{steal} + + $res->{idle} + + $res->{guest} + + $res->{guest_nice}; + $res->{ctime} = $ctime; $res->{cpu} = 0; $res->{wait} = 0; @@ -146,11 +215,15 @@ sub read_proc_stat { if ($diff > 1000) { # don't update too often my $useddiff = $res->{used} - $last_proc_stat->{used}; $useddiff = $diff if $useddiff > $diff; - $res->{cpu} = $useddiff/$diff; + + my $totaldiff = $res->{total} - $last_proc_stat->{total}; + $totaldiff = $diff if $totaldiff > $diff; + + $res->{cpu} = $useddiff/$totaldiff; my $waitdiff = $res->{iowait} - $last_proc_stat->{iowait}; $waitdiff = $diff if $waitdiff > $diff; - $res->{wait} = $waitdiff/$diff; + $res->{wait} = $waitdiff/$totaldiff; $last_proc_stat = $res; } else { @@ -211,6 +284,7 @@ sub read_meminfo { swaptotal => 0, swapfree => 0, swapused => 0, + arcsize => 0, }; my $fh = IO::File->new ("/proc/meminfo", "r"); @@ -232,9 +306,14 @@ sub read_meminfo { $res->{swapfree} = $d->{swapfree}; $res->{swapused} = $res->{swaptotal} - $res->{swapfree}; - my $spages = PVE::Tools::file_read_firstline("/sys/kernel/mm/ksm/pages_sharing"); + my $spages = PVE::Tools::file_read_firstline("/sys/kernel/mm/ksm/pages_sharing") // 0 ; $res->{memshared} = int($spages) * 4096; + my $arc_stats = eval { PVE::Tools::file_get_contents("/proc/spl/kstat/zfs/arcstats") }; + if ($arc_stats && $arc_stats =~ m/^size\s+\d+\s+(\d+)$/m) { + $res->{arcsize} = int($1); + } + return $res; } @@ -280,10 +359,10 @@ sub read_proc_net_dev { sub write_proc_entry { my ($filename, $data) = @_;# - my $fh = IO::File->new($filename, O_WRONLY); + my $fh = IO::File->new($filename, O_WRONLY); die "unable to open file '$filename' - $!\n" if !$fh; - die "unable to write '$filename' - $!\n" unless print $fh $data; - die "closing file '$filename' failed - $!\n" unless close $fh; + print $fh $data or die "unable to write '$filename' - $!\n"; + close $fh or die "closing file '$filename' failed - $!\n"; $fh->close(); } @@ -327,6 +406,7 @@ sub decode_mount { sub parse_mounts { my ($mounts) = @_; + my $mntent = []; while ($mounts =~ /^\s*([^#].*)$/gm) { # lines from the file are encoded so we can just split at spaces @@ -335,11 +415,14 @@ sub parse_mounts { # in glibc's parser frequency and pass seem to be optional $freq = $1 if $opts =~ s/\s+(\d+)$//; $passno = $1 if $opts =~ s/\s+(\d+)$//; - push @$mntent, [decode_mount($what), - decode_mount($dir), - decode_mount($fstype), - decode_mount($opts), - $freq, $passno]; + push @$mntent, [ + decode_mount($what), + decode_mount($dir), + decode_mount($fstype), + decode_mount($opts), + $freq, + $passno, + ]; } return $mntent; }