updateUsageLocked attempts to detect commitment of previously-uncommitted pages, and updates memory accounting to reflect newly-committed pages. Precondition: f.mu must be held; it may be unlocked and reacquired. +checklocks:f.mu
(memCgIDs map[uint32]struct{})
| 1603 | // Precondition: f.mu must be held; it may be unlocked and reacquired. |
| 1604 | // +checklocks:f.mu |
| 1605 | func (f *MemoryFile) updateUsageLocked(memCgIDs map[uint32]struct{}) error { |
| 1606 | // Track if anything changed to elide the merge. |
| 1607 | changedAny := false |
| 1608 | defer func() { |
| 1609 | if changedAny { |
| 1610 | f.memAcct.MergeAll() |
| 1611 | } |
| 1612 | }() |
| 1613 | |
| 1614 | // Reused mincore buffer. |
| 1615 | var buf []byte |
| 1616 | |
| 1617 | maseg := f.memAcct.FirstSegment() |
| 1618 | unscannedStart := uint64(0) |
| 1619 | for maseg.Ok() { |
| 1620 | ma := maseg.ValuePtr() |
| 1621 | if ma.wasteOrReleasing { |
| 1622 | // Skip scanning of waste and releasing pages. This isn't |
| 1623 | // necessarily correct, since !knownCommitted may have become |
| 1624 | // committed after the last call to updateUsageLocked(), then |
| 1625 | // transitioned from used to waste. However, this is consistent |
| 1626 | // with legacy behavior. |
| 1627 | maseg = maseg.NextSegment() |
| 1628 | continue |
| 1629 | } |
| 1630 | if ma.knownCommitted { |
| 1631 | // Known-committed pages remain known-committed until they are |
| 1632 | // decommitted. |
| 1633 | maseg = maseg.NextSegment() |
| 1634 | continue |
| 1635 | } |
| 1636 | |
| 1637 | // Scan the pages of the given memCgID only. This will avoid scanning |
| 1638 | // the whole memory file when the memory usage is required only for a |
| 1639 | // specific cgroup. The total memory usage of all cgroups can be |
| 1640 | // obtained when memCgIDs is nil. |
| 1641 | if memCgIDs != nil { |
| 1642 | if _, ok := memCgIDs[ma.memCgID]; !ok { |
| 1643 | maseg = maseg.NextSegment() |
| 1644 | continue |
| 1645 | } |
| 1646 | } |
| 1647 | |
| 1648 | fr := maseg.Range() |
| 1649 | if fr.Start < unscannedStart { |
| 1650 | fr.Start = unscannedStart |
| 1651 | } |
| 1652 | var checkErr error |
| 1653 | f.forEachChunk(fr, func(chunk *chunkInfo, chunkFR memmap.FileRange) bool { |
| 1654 | s := chunk.sliceAt(chunkFR) |
| 1655 | |
| 1656 | // Ensure that we have sufficient buffer for the call (one byte per |
| 1657 | // page). The length of s must be page-aligned. |
| 1658 | bufLen := len(s) / hostarch.PageSize |
| 1659 | if len(buf) < bufLen { |
| 1660 | buf = make([]byte, bufLen) |
| 1661 | } |
| 1662 |
no test coverage detected