gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/ring0/pagetables/pcids.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  import (
    18  	"gvisor.dev/gvisor/pkg/sync"
    19  )
    20  
    21  // PCIDs is a simple PCID database.
    22  //
    23  // This is not protected by locks and is thus suitable for use only with a
    24  // single CPU at a time.
    25  type PCIDs struct {
    26  	// mu protects below.
    27  	mu sync.Mutex
    28  
    29  	// cache are the assigned page tables.
    30  	cache map[*PageTables]uint16
    31  
    32  	// avail are available PCIDs.
    33  	avail []uint16
    34  }
    35  
    36  // NewPCIDs returns a new PCID database.
    37  //
    38  // start is the first index to assign. Typically this will be one, as the zero
    39  // pcid will always be flushed on transition (see pagetables_x86.go). This may
    40  // be more than one if specific PCIDs are reserved.
    41  //
    42  // Nil is returned iff the start and size are out of range.
    43  func NewPCIDs(start, size uint16) *PCIDs {
    44  	if start+uint16(size) > limitPCID {
    45  		return nil // See comment.
    46  	}
    47  	p := &PCIDs{
    48  		cache: make(map[*PageTables]uint16),
    49  	}
    50  	for pcid := start; pcid < start+size; pcid++ {
    51  		p.avail = append(p.avail, pcid)
    52  	}
    53  	return p
    54  }
    55  
    56  // Assign assigns a PCID to the given PageTables.
    57  //
    58  // This may overwrite any previous assignment provided. If this in the case,
    59  // true is returned to indicate that the PCID should be flushed.
    60  func (p *PCIDs) Assign(pt *PageTables) (uint16, bool) {
    61  	p.mu.Lock()
    62  	if pcid, ok := p.cache[pt]; ok {
    63  		p.mu.Unlock()
    64  		return pcid, false // No flush.
    65  	}
    66  
    67  	// Is there something available?
    68  	if len(p.avail) > 0 {
    69  		pcid := p.avail[len(p.avail)-1]
    70  		p.avail = p.avail[:len(p.avail)-1]
    71  		p.cache[pt] = pcid
    72  
    73  		// We need to flush because while this is in the available
    74  		// pool, it may have been used previously.
    75  		p.mu.Unlock()
    76  		return pcid, true
    77  	}
    78  
    79  	// Evict an existing table.
    80  	for old, pcid := range p.cache {
    81  		delete(p.cache, old)
    82  		p.cache[pt] = pcid
    83  
    84  		// A flush is definitely required in this case, these page
    85  		// tables may still be active. (They will just be assigned some
    86  		// other PCID if and when they hit the given CPU again.)
    87  		p.mu.Unlock()
    88  		return pcid, true
    89  	}
    90  
    91  	// No PCID.
    92  	p.mu.Unlock()
    93  	return 0, false
    94  }
    95  
    96  // Drop drops references to a set of page tables.
    97  func (p *PCIDs) Drop(pt *PageTables) {
    98  	p.mu.Lock()
    99  	if pcid, ok := p.cache[pt]; ok {
   100  		delete(p.cache, pt)
   101  		p.avail = append(p.avail, pcid)
   102  	}
   103  	p.mu.Unlock()
   104  }