Preconditions: f.mu must be locked; it may be unlocked and reacquired.
(fr memmap.FileRange, huge bool)
| 1307 | |
| 1308 | // Preconditions: f.mu must be locked; it may be unlocked and reacquired. |
| 1309 | func (f *MemoryFile) releaseLocked(fr memmap.FileRange, huge bool) { |
| 1310 | defer func() { |
| 1311 | maseg := f.memAcct.LowerBoundSegmentSplitBefore(fr.Start) |
| 1312 | for maseg.Ok() && maseg.Start() < fr.End { |
| 1313 | maseg = f.memAcct.SplitAfter(maseg, fr.End) |
| 1314 | ma := maseg.ValuePtr() |
| 1315 | if ma.kind != usage.System { |
| 1316 | panic(fmt.Sprintf("waste pages %v have unexpected kind %v\n%s", maseg.Range(), ma.kind, f.stringLocked())) |
| 1317 | } |
| 1318 | if ma.knownCommitted { |
| 1319 | malen := maseg.Range().Length() |
| 1320 | f.knownCommittedBytes -= malen |
| 1321 | if !f.opts.DisableMemoryAccounting { |
| 1322 | usage.MemoryAccounting.Dec(malen, ma.kind, ma.memCgID) |
| 1323 | } |
| 1324 | } |
| 1325 | maseg = f.memAcct.Remove(maseg).NextSegment() |
| 1326 | } |
| 1327 | }() |
| 1328 | |
| 1329 | if !huge { |
| 1330 | // Decommit the range being released, then mark the released range as |
| 1331 | // freed. |
| 1332 | f.mu.Unlock() |
| 1333 | f.decommitOrManuallyZero(fr) |
| 1334 | f.mu.Lock() |
| 1335 | f.unfreeSmall.RemoveFullRange(fr) |
| 1336 | return |
| 1337 | } |
| 1338 | |
| 1339 | // Handle huge pages and sub-release. |
| 1340 | |
| 1341 | firstHugeStart := hostarch.HugePageRoundDown(fr.Start) |
| 1342 | lastHugeStart := hostarch.HugePageRoundDown(fr.End - 1) |
| 1343 | firstHugeEnd := firstHugeStart + hostarch.HugePageSize |
| 1344 | lastHugeEnd := lastHugeStart + hostarch.HugePageSize |
| 1345 | if firstHugeStart == lastHugeStart { |
| 1346 | // All of fr falls within a single huge page. |
| 1347 | oldSubrel := f.subreleased[firstHugeStart] |
| 1348 | incSubrel := fr.Length() / hostarch.PageSize |
| 1349 | newSubrel := oldSubrel + incSubrel |
| 1350 | if newSubrel == pagesPerHugePage { |
| 1351 | // Free this huge page. |
| 1352 | // |
| 1353 | // When a small page within a hugepage-backed allocation is |
| 1354 | // individually deallocated (becomes waste), we decommit it to |
| 1355 | // reduce memory usage (and for consistency with legacy behavior). |
| 1356 | // This requires the host to split the containing huge page, if one |
| 1357 | // exists. khugepaged may later re-assemble the containing huge |
| 1358 | // page, implicitly re-committing previously-decommitted small |
| 1359 | // pages as a result. |
| 1360 | // |
| 1361 | // Thus: When a huge page is freed, ensure that the whole huge page |
| 1362 | // is decommitted rather than just the final small page(s), to |
| 1363 | // ensure that we leave behind an uncommitted hugepage-sized range |
| 1364 | // with no re-committed small pages. |
| 1365 | if oldSubrel != 0 { |
| 1366 | delete(f.subreleased, firstHugeStart) |
no test coverage detected