@@ -19,7 +19,6 @@ package fs
1919
2020import (
2121 "bufio"
22- "bytes"
2322 "fmt"
2423 "io/ioutil"
2524 "os"
@@ -30,7 +29,6 @@ import (
3029 "strconv"
3130 "strings"
3231 "syscall"
33- "time"
3432
3533 "github.com/docker/docker/pkg/mount"
3634 "github.com/google/cadvisor/devicemapper"
@@ -47,26 +45,6 @@ const (
4745 LabelCrioImages = "crio-images"
4846)
4947
50- // The maximum number of `du` and `find` tasks that can be running at once.
51- const maxConcurrentOps = 20
52-
53- // A pool for restricting the number of consecutive `du` and `find` tasks running.
54- var pool = make (chan struct {}, maxConcurrentOps )
55-
56- func init () {
57- for i := 0 ; i < maxConcurrentOps ; i ++ {
58- releaseToken ()
59- }
60- }
61-
62- func claimToken () {
63- <- pool
64- }
65-
66- func releaseToken () {
67- pool <- struct {}{}
68- }
69-
7048type partition struct {
7149 mountpoint string
7250 major uint
@@ -559,78 +537,62 @@ func (self *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) {
559537 return nil , fmt .Errorf ("could not find device with major: %d, minor: %d in cached partitions map" , major , minor )
560538}
561539
562- func (self * RealFsInfo ) GetDirDiskUsage (dir string , timeout time.Duration ) (uint64 , error ) {
563- claimToken ()
564- defer releaseToken ()
565- return GetDirDiskUsage (dir , timeout )
566- }
540+ func (self * RealFsInfo ) GetDirUsage (dir string ) (UsageInfo , error ) {
541+ var usage UsageInfo
567542
568- func GetDirDiskUsage (dir string , timeout time.Duration ) (uint64 , error ) {
569543 if dir == "" {
570- return 0 , fmt .Errorf ("invalid directory" )
571- }
572- cmd := exec .Command ("ionice" , "-c3" , "nice" , "-n" , "19" , "du" , "-s" , dir )
573- stdoutp , err := cmd .StdoutPipe ()
574- if err != nil {
575- return 0 , fmt .Errorf ("failed to setup stdout for cmd %v - %v" , cmd .Args , err )
576- }
577- stderrp , err := cmd .StderrPipe ()
578- if err != nil {
579- return 0 , fmt .Errorf ("failed to setup stderr for cmd %v - %v" , cmd .Args , err )
544+ return usage , fmt .Errorf ("invalid directory" )
580545 }
581546
582- if err := cmd .Start (); err != nil {
583- return 0 , fmt .Errorf ("failed to exec du - %v" , err )
584- }
585- timer := time .AfterFunc (timeout , func () {
586- klog .Warningf ("Killing cmd %v due to timeout(%s)" , cmd .Args , timeout .String ())
587- cmd .Process .Kill ()
588- })
589- stdoutb , souterr := ioutil .ReadAll (stdoutp )
590- if souterr != nil {
591- klog .Errorf ("Failed to read from stdout for cmd %v - %v" , cmd .Args , souterr )
592- }
593- stderrb , _ := ioutil .ReadAll (stderrp )
594- err = cmd .Wait ()
595- timer .Stop ()
547+ rootInfo , err := os .Stat (dir )
596548 if err != nil {
597- return 0 , fmt .Errorf ("du command failed on %s with output stdout: %s, stderr : %s - % v" , dir , string ( stdoutb ), string ( stderrb ) , err )
549+ return usage , fmt .Errorf ("could not stat %q to get inode usage : %v" , dir , err )
598550 }
599- stdout := string ( stdoutb )
600- usageInKb , err := strconv . ParseUint ( strings . Fields ( stdout )[ 0 ], 10 , 64 )
601- if err != nil {
602- return 0 , fmt .Errorf ("cannot parse 'du' output %s - %s " , stdout , err )
551+
552+ rootStat , ok := rootInfo . Sys ().( * syscall. Stat_t )
553+ if ! ok {
554+ return usage , fmt .Errorf ("unsuported fileinfo for getting inode usage of %q " , dir )
603555 }
604- return usageInKb * 1024 , nil
605- }
606556
607- func (self * RealFsInfo ) GetDirInodeUsage (dir string , timeout time.Duration ) (uint64 , error ) {
608- claimToken ()
609- defer releaseToken ()
610- return GetDirInodeUsage (dir , timeout )
611- }
557+ rootDevId := rootStat .Dev
612558
613- func GetDirInodeUsage (dir string , timeout time.Duration ) (uint64 , error ) {
614- if dir == "" {
615- return 0 , fmt .Errorf ("invalid directory" )
616- }
617- var counter byteCounter
618- var stderr bytes.Buffer
619- findCmd := exec .Command ("ionice" , "-c3" , "nice" , "-n" , "19" , "find" , dir , "-xdev" , "-printf" , "." )
620- findCmd .Stdout , findCmd .Stderr = & counter , & stderr
621- if err := findCmd .Start (); err != nil {
622- return 0 , fmt .Errorf ("failed to exec cmd %v - %v; stderr: %v" , findCmd .Args , err , stderr .String ())
623- }
624- timer := time .AfterFunc (timeout , func () {
625- klog .Warningf ("Killing cmd %v due to timeout(%s)" , findCmd .Args , timeout .String ())
626- findCmd .Process .Kill ()
559+ // dedupedInode stores inodes that could be duplicates (nlink > 1)
560+ dedupedInodes := make (map [uint64 ]struct {})
561+
562+ err = filepath .Walk (dir , func (path string , info os.FileInfo , err error ) error {
563+ if os .IsNotExist (err ) {
564+ // expected if files appear/vanish
565+ return nil
566+ }
567+ if err != nil {
568+ return fmt .Errorf ("unable to count inodes for part of dir %s: %s" , dir , err )
569+ }
570+
571+ s , ok := info .Sys ().(* syscall.Stat_t )
572+ if ! ok {
573+ return fmt .Errorf ("unsupported fileinfo; could not convert to stat_t" )
574+ }
575+
576+ if s .Dev != rootDevId {
577+ // don't descend into directories on other devices
578+ return filepath .SkipDir
579+ }
580+ if s .Nlink > 1 {
581+ if _ , ok := dedupedInodes [s .Ino ]; ! ok {
582+ // Dedupe things that could be hardlinks
583+ dedupedInodes [s .Ino ] = struct {}{}
584+
585+ usage .Bytes += uint64 (s .Blocks ) * uint64 (512 )
586+ usage .Inodes ++
587+ }
588+ } else {
589+ usage .Bytes += uint64 (s .Blocks ) * uint64 (512 )
590+ usage .Inodes ++
591+ }
592+ return nil
627593 })
628- err := findCmd .Wait ()
629- timer .Stop ()
630- if err != nil {
631- return 0 , fmt .Errorf ("cmd %v failed. stderr: %s; err: %v" , findCmd .Args , stderr .String (), err )
632- }
633- return counter .bytesWritten , nil
594+
595+ return usage , nil
634596}
635597
636598func getVfsStats (path string ) (total uint64 , free uint64 , avail uint64 , inodes uint64 , inodesFree uint64 , err error ) {
0 commit comments