gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/runsc/boot/limits.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 boot 16 17 import ( 18 "fmt" 19 20 specs "github.com/opencontainers/runtime-spec/specs-go" 21 "golang.org/x/sys/unix" 22 "gvisor.dev/gvisor/pkg/log" 23 "gvisor.dev/gvisor/pkg/sentry/limits" 24 "gvisor.dev/gvisor/pkg/sync" 25 ) 26 27 func findName(lt limits.LimitType) string { 28 for k, v := range limits.FromLinuxResourceName { 29 if v == lt { 30 return k 31 } 32 } 33 return "unknown" 34 } 35 36 var defaults defs 37 38 type defs struct { 39 mu sync.Mutex 40 set *limits.LimitSet 41 err error 42 } 43 44 func (d *defs) get() (*limits.LimitSet, error) { 45 d.mu.Lock() 46 defer d.mu.Unlock() 47 48 if d.err != nil { 49 return nil, d.err 50 } 51 if d.set == nil { 52 if err := d.initDefaults(); err != nil { 53 d.err = err 54 return nil, err 55 } 56 } 57 return d.set, nil 58 } 59 60 func (d *defs) initDefaults() error { 61 ls, err := limits.NewLinuxLimitSet() 62 if err != nil { 63 return err 64 } 65 66 // Set default limits based on what containers get by default, ex: 67 // $ docker run --rm debian prlimit 68 ls.SetUnchecked(limits.AS, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 69 ls.SetUnchecked(limits.Core, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 70 ls.SetUnchecked(limits.CPU, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 71 ls.SetUnchecked(limits.Data, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 72 ls.SetUnchecked(limits.FileSize, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 73 ls.SetUnchecked(limits.Locks, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 74 ls.SetUnchecked(limits.MemoryLocked, limits.Limit{Cur: 65536, Max: 65536}) 75 ls.SetUnchecked(limits.MessageQueueBytes, limits.Limit{Cur: 819200, Max: 819200}) 76 ls.SetUnchecked(limits.Nice, limits.Limit{Cur: 0, Max: 0}) 77 ls.SetUnchecked(limits.NumberOfFiles, limits.Limit{Cur: 1048576, Max: 1048576}) 78 ls.SetUnchecked(limits.ProcessCount, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 79 ls.SetUnchecked(limits.Rss, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 80 ls.SetUnchecked(limits.RealTimePriority, limits.Limit{Cur: 0, Max: 0}) 81 ls.SetUnchecked(limits.Rttime, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 82 ls.SetUnchecked(limits.SignalsPending, limits.Limit{Cur: 0, Max: 0}) 83 ls.SetUnchecked(limits.Stack, limits.Limit{Cur: 8388608, Max: limits.Infinity}) 84 85 // Read host limits that directly affect the sandbox and adjust the defaults 86 // based on them. 87 for _, res := range []int{unix.RLIMIT_FSIZE, unix.RLIMIT_NOFILE} { 88 var hl unix.Rlimit 89 if err := unix.Getrlimit(res, &hl); err != nil { 90 return err 91 } 92 93 lt, ok := limits.FromLinuxResource[res] 94 if !ok { 95 return fmt.Errorf("unknown rlimit type %v", res) 96 } 97 hostLimit := limits.Limit{ 98 Cur: limits.FromLinux(hl.Cur), 99 Max: limits.FromLinux(hl.Max), 100 } 101 102 defaultLimit := ls.Get(lt) 103 if hostLimit.Cur != limits.Infinity && hostLimit.Cur < defaultLimit.Cur { 104 log.Warningf("Host limit is lower than recommended, resource: %q, host: %d, recommended: %d", findName(lt), hostLimit.Cur, defaultLimit.Cur) 105 } 106 if hostLimit.Cur != defaultLimit.Cur || hostLimit.Max != defaultLimit.Max { 107 log.Infof("Setting limit from host, resource: %q {soft: %d, hard: %d}", findName(lt), hostLimit.Cur, hostLimit.Max) 108 ls.SetUnchecked(lt, hostLimit) 109 } 110 } 111 112 d.set = ls 113 return nil 114 } 115 116 func createLimitSet(spec *specs.Spec, enableTPUProxy bool) (*limits.LimitSet, error) { 117 ls, err := defaults.get() 118 if err != nil { 119 return nil, err 120 } 121 // Set RLIMIT_MEMLOCK's default value to unlimited when TPUProxy is enabled. 122 // The value will be overwritten if the exact rlimit is provided. 123 if enableTPUProxy { 124 ls.SetUnchecked(limits.MemoryLocked, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 125 } 126 // Then apply overwrites on top of defaults. 127 for _, rl := range spec.Process.Rlimits { 128 lt, ok := limits.FromLinuxResourceName[rl.Type] 129 if !ok { 130 return nil, fmt.Errorf("unknown resource %q", rl.Type) 131 } 132 ls.SetUnchecked(lt, limits.Limit{ 133 Cur: rl.Soft, 134 Max: rl.Hard, 135 }) 136 } 137 return ls, nil 138 }