@@ -69,6 +69,25 @@ func newCgroupV2Statter(fs afero.Fs, path string, depth int) (*cgroupV2Statter,
6969 }, nil
7070}
7171
72+ // getFromParentOrFallback attempts to get a value from the parent cgroup
73+ // when the current cgroup has no value set. If there is no parent, it
74+ // calls the fallback function to determine the return value.
75+ func getFromParentOrFallback [T any ](
76+ s * cgroupV2Statter ,
77+ getter func (* cgroupV2Statter ) (T , error ),
78+ fallback func () (T , error ),
79+ ) (T , error ) {
80+ if s .parent != nil {
81+ result , err := getter (s .parent )
82+ if err != nil {
83+ var zero T
84+ return zero , xerrors .Errorf ("read parent: %w" , err )
85+ }
86+ return result , nil
87+ }
88+ return fallback ()
89+ }
90+
7291func (s cgroupV2Statter ) cpuUsed () (used float64 , err error ) {
7392 cpuStatPath := filepath .Join (s .path , cgroupV2CPUStat )
7493
@@ -94,17 +113,10 @@ func (s cgroupV2Statter) cpuQuota() (float64, error) {
94113 }
95114
96115 // If the value is not a valid integer, assume it is the string
97- // 'max' and that there is no limit set. In this scenario, we call
98- // the parent to find its quota.
99- if s .parent != nil {
100- total , err := s .parent .cpuQuota ()
101- if err != nil {
102- return 0 , xerrors .Errorf ("get parent cpu quota: %w" , err )
103- }
104- return total , nil
105- }
106-
107- return - 1 , nil
116+ // 'max' and that there is no limit set. Try the parent or return -1.
117+ return getFromParentOrFallback (& s , (* cgroupV2Statter ).cpuQuota , func () (float64 , error ) {
118+ return - 1.0 , nil
119+ })
108120 }
109121
110122 return float64 (quotaUs ), nil
@@ -120,18 +132,11 @@ func (s cgroupV2Statter) cpuPeriod() (float64, error) {
120132 }
121133
122134 // If the value is not a valid integer or the cpu.max file does
123- // not exist, we call the parent to find its period. This can happen
124- // in system-level cgroups like init.scope where cpu.max may not exist.
125- if s .parent != nil {
126- period , err := s .parent .cpuPeriod ()
127- if err != nil {
128- return 0 , xerrors .Errorf ("get parent cpu period: %w" , err )
129- }
130- return period , nil
131- }
132-
133- // No parent and no period found in the cgroup hierarchy.
134- return cgroupV2DefaultPeriodUs , nil
135+ // not exist, try the parent or return the default period. This can
136+ // happen in system-level cgroups like init.scope where cpu.max may not exist.
137+ return getFromParentOrFallback (& s , (* cgroupV2Statter ).cpuPeriod , func () (float64 , error ) {
138+ return cgroupV2DefaultPeriodUs , nil
139+ })
135140 }
136141
137142 return float64 (periodUs ), nil
@@ -161,19 +166,11 @@ func (s cgroupV2Statter) memoryMaxBytes() (*float64, error) {
161166 }
162167
163168 // If the value is not a valid integer _or_ the memory max file
164- // does not exist, than we can assume that the limit is 'max'.
165- // If the memory limit is max, and we have a parent, we shall call
166- // the parent to find its maximum memory value.
167- if s .parent != nil {
168- result , err := s .parent .memoryMaxBytes ()
169- if err != nil {
170- return nil , xerrors .Errorf ("read parent memory max: %w" , err )
171- }
172- return result , nil
173- }
174-
175- // We have no parent, and no max memory limit, so there is no memory limit.
176- return nil , nil
169+ // does not exist, we can assume that the limit is 'max'.
170+ // Try the parent or return nil (no limit).
171+ return getFromParentOrFallback (& s , (* cgroupV2Statter ).memoryMaxBytes , func () (* float64 , error ) {
172+ return nil , nil
173+ })
177174 }
178175
179176 return ptr .To (float64 (maxUsageBytes )), nil
@@ -188,18 +185,10 @@ func (s cgroupV2Statter) memoryCurrentBytes() (int64, error) {
188185 return 0 , xerrors .Errorf ("read memory current: %w" , err )
189186 }
190187
191- // If the memory current file does not exist, and we have a parent,
192- // we shall call the parent to find its current memory value.
193- if s .parent != nil {
194- result , err := s .parent .memoryCurrentBytes ()
195- if err != nil {
196- return 0 , xerrors .Errorf ("read parent memory current: %w" , err )
197- }
198- return result , nil
199- }
200-
201- // We have no parent, and no memory current file, so return the error.
202- return 0 , xerrors .Errorf ("read memory current: %w" , err )
188+ // If the memory current file does not exist, try the parent or return error.
189+ return getFromParentOrFallback (& s , (* cgroupV2Statter ).memoryCurrentBytes , func () (int64 , error ) {
190+ return 0 , xerrors .Errorf ("read memory current: %w" , err )
191+ })
203192 }
204193
205194 return currUsageBytes , nil
@@ -214,18 +203,10 @@ func (s cgroupV2Statter) memoryInactiveFileBytes() (int64, error) {
214203 return 0 , xerrors .Errorf ("read memory stat inactive_file: %w" , err )
215204 }
216205
217- // If the memory stat file does not exist, and we have a parent,
218- // we shall call the parent to find its inactive_file value.
219- if s .parent != nil {
220- result , err := s .parent .memoryInactiveFileBytes ()
221- if err != nil {
222- return 0 , xerrors .Errorf ("read parent memory stat inactive_file: %w" , err )
223- }
224- return result , nil
225- }
226-
227- // We have no parent, and no memory stat file, so return the error.
228- return 0 , xerrors .Errorf ("read memory stat inactive_file: %w" , err )
206+ // If the memory stat file does not exist, try the parent or return error.
207+ return getFromParentOrFallback (& s , (* cgroupV2Statter ).memoryInactiveFileBytes , func () (int64 , error ) {
208+ return 0 , xerrors .Errorf ("read memory stat inactive_file: %w" , err )
209+ })
229210 }
230211
231212 return inactiveFileBytes , nil
0 commit comments