2018-04-27 17:37:02 +00:00
|
|
|
// Copyright 2018 Google Inc.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
// +build i386 amd64
|
|
|
|
|
|
|
|
package pagetables
|
|
|
|
|
2018-08-09 04:27:58 +00:00
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
2018-06-07 05:51:58 +00:00
|
|
|
// limitPCID is the number of valid PCIDs.
|
|
|
|
const limitPCID = 4096
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// PCIDs is a simple PCID database.
|
2018-06-07 05:51:58 +00:00
|
|
|
//
|
|
|
|
// This is not protected by locks and is thus suitable for use only with a
|
|
|
|
// single CPU at a time.
|
2018-04-27 17:37:02 +00:00
|
|
|
type PCIDs struct {
|
2018-08-09 04:27:58 +00:00
|
|
|
// mu protects below.
|
|
|
|
mu sync.Mutex
|
|
|
|
|
2018-06-07 05:51:58 +00:00
|
|
|
// cache are the assigned page tables.
|
|
|
|
cache map[*PageTables]uint16
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2018-06-07 05:51:58 +00:00
|
|
|
// avail are available PCIDs.
|
|
|
|
avail []uint16
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2018-06-07 05:51:58 +00:00
|
|
|
// NewPCIDs returns a new PCID database.
|
|
|
|
//
|
|
|
|
// start is the first index to assign. Typically this will be one, as the zero
|
|
|
|
// pcid will always be flushed on transition (see pagetables_x86.go). This may
|
|
|
|
// be more than one if specific PCIDs are reserved.
|
|
|
|
//
|
|
|
|
// Nil is returned iff the start and size are out of range.
|
|
|
|
func NewPCIDs(start, size uint16) *PCIDs {
|
|
|
|
if start+uint16(size) >= limitPCID {
|
|
|
|
return nil // See comment.
|
|
|
|
}
|
|
|
|
p := &PCIDs{
|
|
|
|
cache: make(map[*PageTables]uint16),
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2018-06-07 05:51:58 +00:00
|
|
|
for pcid := start; pcid < start+size; pcid++ {
|
|
|
|
p.avail = append(p.avail, pcid)
|
|
|
|
}
|
|
|
|
return p
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2018-06-07 05:51:58 +00:00
|
|
|
// Assign assigns a PCID to the given PageTables.
|
|
|
|
//
|
|
|
|
// This may overwrite any previous assignment provided. If this in the case,
|
|
|
|
// true is returned to indicate that the PCID should be flushed.
|
|
|
|
func (p *PCIDs) Assign(pt *PageTables) (uint16, bool) {
|
2018-08-09 04:27:58 +00:00
|
|
|
p.mu.Lock()
|
2018-06-07 05:51:58 +00:00
|
|
|
if pcid, ok := p.cache[pt]; ok {
|
2018-08-09 04:27:58 +00:00
|
|
|
p.mu.Unlock()
|
2018-06-07 05:51:58 +00:00
|
|
|
return pcid, false // No flush.
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2018-06-07 05:51:58 +00:00
|
|
|
|
|
|
|
// Is there something available?
|
|
|
|
if len(p.avail) > 0 {
|
|
|
|
pcid := p.avail[len(p.avail)-1]
|
|
|
|
p.avail = p.avail[:len(p.avail)-1]
|
2018-08-06 17:10:25 +00:00
|
|
|
p.cache[pt] = pcid
|
2018-06-07 05:51:58 +00:00
|
|
|
|
|
|
|
// We need to flush because while this is in the available
|
|
|
|
// pool, it may have been used previously.
|
2018-08-09 04:27:58 +00:00
|
|
|
p.mu.Unlock()
|
2018-06-07 05:51:58 +00:00
|
|
|
return pcid, true
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2018-06-07 05:51:58 +00:00
|
|
|
|
|
|
|
// Evict an existing table.
|
|
|
|
for old, pcid := range p.cache {
|
|
|
|
delete(p.cache, old)
|
|
|
|
p.cache[pt] = pcid
|
|
|
|
|
|
|
|
// A flush is definitely required in this case, these page
|
|
|
|
// tables may still be active. (They will just be assigned some
|
|
|
|
// other PCID if and when they hit the given CPU again.)
|
2018-08-09 04:27:58 +00:00
|
|
|
p.mu.Unlock()
|
2018-06-07 05:51:58 +00:00
|
|
|
return pcid, true
|
|
|
|
}
|
|
|
|
|
|
|
|
// No PCID.
|
2018-08-09 04:27:58 +00:00
|
|
|
p.mu.Unlock()
|
2018-06-07 05:51:58 +00:00
|
|
|
return 0, false
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2018-06-07 05:51:58 +00:00
|
|
|
// Drop drops references to a set of page tables.
|
|
|
|
func (p *PCIDs) Drop(pt *PageTables) {
|
2018-08-09 04:27:58 +00:00
|
|
|
p.mu.Lock()
|
2018-06-07 05:51:58 +00:00
|
|
|
if pcid, ok := p.cache[pt]; ok {
|
|
|
|
delete(p.cache, pt)
|
|
|
|
p.avail = append(p.avail, pcid)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2018-08-09 04:27:58 +00:00
|
|
|
p.mu.Unlock()
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|