github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/sysadvisor/plugin/qosaware/server/helper.go (about) 1 /* 2 Copyright 2022 The Katalyst Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package server 18 19 import ( 20 "fmt" 21 22 "k8s.io/apimachinery/pkg/util/uuid" 23 24 "github.com/kubewharf/katalyst-core/pkg/agent/qrm-plugins/cpu/dynamicpolicy/cpuadvisor" 25 "github.com/kubewharf/katalyst-core/pkg/agent/sysadvisor/types" 26 ) 27 28 // NewPoolCalculationEntries returns CalculationEntries, 29 // and it will only fill up with OwnerPoolName and leaves numa info empty. 30 func NewPoolCalculationEntries(poolName string) *cpuadvisor.CalculationEntries { 31 ci := &cpuadvisor.CalculationInfo{ 32 OwnerPoolName: poolName, 33 CalculationResultsByNumas: make(map[int64]*cpuadvisor.NumaCalculationResult), 34 } 35 36 return &cpuadvisor.CalculationEntries{Entries: map[string]*cpuadvisor.CalculationInfo{"": ci}} 37 } 38 39 // NewBlock constructs a Block structure; generate a new one if blockID is missed 40 func NewBlock(size uint64, blockID string) *cpuadvisor.Block { 41 if blockID == "" { 42 blockID = string(uuid.NewUUID()) 43 } 44 45 return &cpuadvisor.Block{ 46 Result: size, 47 OverlapTargets: make([]*cpuadvisor.OverlapTarget, 0), 48 BlockId: blockID, 49 } 50 } 51 52 // internalBlock works as a packed structure for Block; 53 // aside for Block, it also stores some extra info to speed up efficiency. 54 type internalBlock struct { 55 // Block stores the standard Block info 56 // - it is thread-safe since it is referred only by this internalBlock 57 Block *cpuadvisor.Block 58 // NumaID stores the numa node that this Block belongs to 59 NumaID int64 60 61 // only one of PoolName and ContainerInfo can be set as valid 62 // PoolName stores pool that owns this block 63 // - it may be nil if this block belongs to pod rather than pool 64 PoolName string 65 // ContainerInfo stores container that own this block 66 // - it may be nil if this block belongs to pool rather than pod 67 // - it is thread-safe since it is deep-copied when constructed internalBlock 68 ContainerInfo *types.ContainerInfo 69 70 // NumaCalculationResult stores 71 // - it is thread-safe since it is referred only by this internalBlock 72 NumaCalculationResult *cpuadvisor.NumaCalculationResult 73 } 74 75 func NewInnerBlock(block *cpuadvisor.Block, numaID int64, poolName string, containerInfo *types.ContainerInfo, 76 numaCalculationResult *cpuadvisor.NumaCalculationResult, 77 ) *internalBlock { 78 ib := &internalBlock{ 79 Block: block, 80 NumaID: numaID, 81 PoolName: poolName, 82 NumaCalculationResult: numaCalculationResult, 83 } 84 85 if containerInfo != nil { 86 ib.ContainerInfo = containerInfo.Clone() 87 } 88 return ib 89 } 90 91 // initialOverlapTarget constructs cpuadvisor.OverlapTarget based on Block info 92 func (ib *internalBlock) initialOverlapTarget() *cpuadvisor.OverlapTarget { 93 target := &cpuadvisor.OverlapTarget{} 94 95 if ib.ContainerInfo == nil { 96 target.OverlapTargetPoolName = ib.PoolName 97 target.OverlapType = cpuadvisor.OverlapType_OverlapWithPool 98 } else { 99 target.OverlapTargetPodUid = ib.ContainerInfo.PodUID 100 target.OverlapTargetContainerName = ib.ContainerInfo.ContainerName 101 target.OverlapType = cpuadvisor.OverlapType_OverlapWithPod 102 } 103 104 return target 105 } 106 107 // for multiple Block in one single NUMA node, join tries to 108 // split the existed Block into more Block to ensure we must 109 // aggregate different Block according to the size of them. 110 func (ib *internalBlock) join(blockID string, set blockSet) { 111 ibList := set.get(blockID) 112 if ibList == nil || len(ibList) == 0 { 113 _ = set.add(ib) 114 return 115 } 116 117 // support to merge current Block into blockSet iff they belong to the same numa node 118 if ib.NumaID != ibList[0].NumaID { 119 return 120 } 121 122 thisTarget := ib.initialOverlapTarget() 123 if ib.Block.Result == ibList[0].Block.Result { 124 // if cpu size equals, it means current Block can share the same; 125 // otherwise, a split Block must be generated to ensure size aggregation 126 127 for _, innerBlock := range ibList { 128 innerBlock.Block.OverlapTargets = append(innerBlock.Block.OverlapTargets, thisTarget) 129 130 target := innerBlock.initialOverlapTarget() 131 ib.Block.OverlapTargets = append(ib.Block.OverlapTargets, target) 132 ib.Block.BlockId = blockID 133 } 134 _ = set.add(ib) 135 } else if ib.Block.Result > ibList[0].Block.Result { 136 // if cpu size for current Block is greater, split current Block into multiple ones 137 // i.e. one to match with cpu size for existing blockSet, one to add as a new blockSet 138 139 newBlock := NewBlock(ibList[0].Block.Result, blockID) 140 newInnerBlock := NewInnerBlock(newBlock, ib.NumaID, ib.PoolName, ib.ContainerInfo, ib.NumaCalculationResult) 141 newInnerBlock.join(blockID, set) 142 143 ib.NumaCalculationResult.Blocks = append(ib.NumaCalculationResult.Blocks, newBlock) 144 ib.Block.Result = ib.Block.Result - ibList[0].Block.Result 145 ib.Block.OverlapTargets = nil 146 _ = set.add(ib) 147 } else { 148 // if cpu size for current Block is smaller, split existing Block in blockSet into multiple ones 149 // i.e. one to match with cpu size for current Block, one to add as a new blockSet 150 151 for _, innerBlock := range ibList { 152 newBlock := NewBlock(ibList[0].Block.Result-ib.Block.Result, "") 153 newInnerBlock := NewInnerBlock(newBlock, innerBlock.NumaID, innerBlock.PoolName, innerBlock.ContainerInfo, innerBlock.NumaCalculationResult) 154 newInnerBlock.join(newBlock.BlockId, set) 155 156 innerBlock.NumaCalculationResult.Blocks = append(innerBlock.NumaCalculationResult.Blocks, newBlock) 157 innerBlock.Block.Result = ib.Block.Result 158 } 159 160 ib.join(ib.Block.BlockId, set) 161 } 162 } 163 164 // blockSet is a global variable to store, and the mapping relation 165 // block id -> the internalBlock that belongs to this block id 166 // 167 // those internalBlock sharing with the same id should satisfy several constraints 168 // - they belong to the same NUMA Node (though there exists no NUMA concepts here) 169 // - they share with the same cpuset pool 170 type blockSet map[string][]*internalBlock 171 172 func NewBlockSet() blockSet { 173 return make(map[string][]*internalBlock) 174 } 175 176 func (bs blockSet) add(ib *internalBlock) error { 177 if ib == nil || ib.Block == nil { 178 return fmt.Errorf("internal block is invalid") 179 } 180 181 internalBlocks, ok := bs[ib.Block.BlockId] 182 if !ok { 183 internalBlocks = make([]*internalBlock, 0) 184 } 185 186 internalBlocks = append(internalBlocks, ib) 187 bs[ib.Block.BlockId] = internalBlocks 188 return nil 189 } 190 191 func (bs blockSet) get(blockID string) []*internalBlock { 192 internalBlocks, ok := bs[blockID] 193 if !ok { 194 return nil 195 } 196 return internalBlocks 197 } 198 199 // getNumaCalculationResult returns numa-level calculation results 200 func getNumaCalculationResult(calculationEntriesMap map[string]*cpuadvisor.CalculationEntries, 201 entryName, containerName string, numa int64, 202 ) (*cpuadvisor.NumaCalculationResult, bool) { 203 entries, ok := calculationEntriesMap[entryName] 204 if !ok { 205 return nil, false 206 } 207 208 return entries.GetNUMACalculationResult(containerName, numa) 209 }