github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/ring0/pagetables/allocator.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package pagetables
    16  
    17  // Allocator is used to allocate and map PTEs.
    18  //
    19  // Note that allocators may be called concurrently.
    20  type Allocator interface {
    21  	// NewPTEs returns a new set of PTEs and their physical address.
    22  	NewPTEs() *PTEs
    23  
    24  	// PhysicalFor gives the physical address for a set of PTEs.
    25  	PhysicalFor(ptes *PTEs) uintptr
    26  
    27  	// LookupPTEs looks up PTEs by physical address.
    28  	LookupPTEs(physical uintptr) *PTEs
    29  
    30  	// FreePTEs marks a set of PTEs a freed, although they may not be available
    31  	// for use again until Recycle is called, below.
    32  	FreePTEs(ptes *PTEs)
    33  
    34  	// Recycle makes freed PTEs available for use again.
    35  	Recycle()
    36  }
    37  
    38  // RuntimeAllocator is a trivial allocator.
    39  type RuntimeAllocator struct {
    40  	// used is the set of PTEs that have been allocated. This includes any
    41  	// PTEs that may be in the pool below. PTEs are only freed from this
    42  	// map by the Drain call.
    43  	//
    44  	// This exists to prevent accidental garbage collection.
    45  	used map[*PTEs]struct{}
    46  
    47  	// pool is the set of free-to-use PTEs.
    48  	pool []*PTEs
    49  
    50  	// freed is the set of recently-freed PTEs.
    51  	freed []*PTEs
    52  }
    53  
    54  // NewRuntimeAllocator returns an allocator that uses runtime allocation.
    55  func NewRuntimeAllocator() *RuntimeAllocator {
    56  	r := new(RuntimeAllocator)
    57  	r.Init()
    58  	return r
    59  }
    60  
    61  // Init initializes a RuntimeAllocator.
    62  func (r *RuntimeAllocator) Init() {
    63  	r.used = make(map[*PTEs]struct{})
    64  }
    65  
    66  // Recycle returns freed pages to the pool.
    67  func (r *RuntimeAllocator) Recycle() {
    68  	r.pool = append(r.pool, r.freed...)
    69  	r.freed = r.freed[:0]
    70  }
    71  
    72  // Drain empties the pool.
    73  func (r *RuntimeAllocator) Drain() {
    74  	r.Recycle()
    75  	for i, ptes := range r.pool {
    76  		// Zap the entry in the underlying array to ensure that it can
    77  		// be properly garbage collected.
    78  		r.pool[i] = nil
    79  		// Similarly, free the reference held by the used map (these
    80  		// also apply for the pool entries).
    81  		delete(r.used, ptes)
    82  	}
    83  	r.pool = r.pool[:0]
    84  }
    85  
    86  // NewPTEs implements Allocator.NewPTEs.
    87  //
    88  // Note that the "physical" address here is actually the virtual address of the
    89  // PTEs structure. The entries are tracked only to avoid garbage collection.
    90  //
    91  // This is guaranteed not to split as long as the pool is sufficiently full.
    92  //
    93  //go:nosplit
    94  func (r *RuntimeAllocator) NewPTEs() *PTEs {
    95  	// Pull from the pool if we can.
    96  	if len(r.pool) > 0 {
    97  		ptes := r.pool[len(r.pool)-1]
    98  		r.pool = r.pool[:len(r.pool)-1]
    99  		return ptes
   100  	}
   101  
   102  	// Allocate a new entry.
   103  	ptes := newAlignedPTEs()
   104  	r.used[ptes] = struct{}{}
   105  	return ptes
   106  }
   107  
   108  // PhysicalFor returns the physical address for the given PTEs.
   109  //
   110  //go:nosplit
   111  func (r *RuntimeAllocator) PhysicalFor(ptes *PTEs) uintptr {
   112  	return physicalFor(ptes)
   113  }
   114  
   115  // LookupPTEs implements Allocator.LookupPTEs.
   116  //
   117  //go:nosplit
   118  func (r *RuntimeAllocator) LookupPTEs(physical uintptr) *PTEs {
   119  	return fromPhysical(physical)
   120  }
   121  
   122  // FreePTEs implements Allocator.FreePTEs.
   123  //
   124  //go:nosplit
   125  func (r *RuntimeAllocator) FreePTEs(ptes *PTEs) {
   126  	r.freed = append(r.freed, ptes)
   127  }