1# SPDX-License-Identifier: BSD-3-Clause 2# Copyright (C) 2021 Intel Corporation. 3# All rights reserved. 4 5check_cgroup() { 6 # Try to work with both, cgroup-v1 and cgroup-v2. Verify which version is 7 # in use by looking up interfaces common for either of the versions. 8 if [[ -e $sysfs_cgroup/cgroup.controllers ]]; then 9 # cgroup2 is mounted, check if cpuset controller is available 10 [[ $(< "$sysfs_cgroup/cgroup.controllers") == *cpuset* ]] && echo 2 11 elif [[ -e $sysfs_cgroup/cpuset/tasks ]]; then 12 # cgroup's cpuset subsystem is mounted 13 echo 1 14 fi || return 1 15} 16 17init_cpuset_cgroup() { 18 local cgroup pid 19 local -A cgroups=() 20 21 # For cgroup-v2 we need to prepare cpuset subsystem on our own 22 if ((cgroup_version == 2)); then 23 set_cgroup_attr / cgroup.subtree_control "+cpuset" 24 create_cgroup /cpuset 25 set_cgroup_attr /cpuset cgroup.subtree_control "+cpuset" 26 # On distros which use cgroup-v2 under systemd, each process is 27 # maintained under separate, pre-configured subtree. With the rule of 28 # "internal processes are not permitted" this means that we won't find 29 # ourselves under subsystem's root, rather on the bottom of the cgroup 30 # maintaining user's session. To recreate the simple /cpuset setup from 31 # v1, move all the threads from all the existing cgroups to the top 32 # cgroup / and then migrate it to the /cpuset we created above. 33 for pid in /proc/+([0-9]); do 34 cgroup=$(get_cgroup "${pid##*/}") || continue 35 [[ $cgroup != / ]] || continue 36 cgroups["$cgroup"]=$cgroup 37 done 2> /dev/null 38 for cgroup in "${!cgroups[@]}"; do 39 move_cgroup_procs "$cgroup" / 40 done 41 # Now, move all the threads to the cpuset 42 move_cgroup_procs / /cpuset 43 elif ((cgroup_version == 1)); then 44 set_cgroup_attr /cpuset cgroup.procs "$$" 45 fi 46} 47 48is_cgroup_threaded() { 49 [[ -e $sysfs_cgroup/$1/cgroup.type ]] || return 1 50 [[ $(< "$sysfs_cgroup/$1/cgroup.type") == threaded ]] 51} 52 53move_cgroup_procs() { 54 local old_cgroup=$1 55 local new_cgroup=$2 56 local proc procs old_proc_interface new_proc_interface 57 58 # If target cgroups don't exist then there's nothing to do. 59 [[ -e $sysfs_cgroup/$old_cgroup ]] || return 0 60 [[ -e $sysfs_cgroup/$new_cgroup ]] || return 0 61 62 old_proc_interface=cgroup.procs 63 new_proc_interface=cgroup.procs 64 if ((cgroup_version == 2)); then 65 if is_cgroup_threaded "$new_cgroup"; then 66 new_proc_interface=cgroup.threads 67 fi 68 if is_cgroup_threaded "$old_cgroup"; then 69 old_proc_interface=cgroup.threads 70 fi 71 fi 72 73 fold_list_onto_array procs $(< "$sysfs_cgroup/$old_cgroup/$old_proc_interface") 74 75 for proc in "${!procs[@]}"; do 76 # We can't move every kernel thread around and every process can 77 # exit at any point so ignore any failures upon writing the 78 # processes out. FIXME: Check PF_KTHREAD instead? 79 [[ -n $(readlink -f "/proc/$proc/exe") ]] || continue 80 echo "$proc" > "$sysfs_cgroup/$new_cgroup/$new_proc_interface" 2> /dev/null || : 81 done 82} 83 84set_cgroup_attr() { 85 local cgroup=$1 86 local attr=$2 87 local val=$3 88 89 [[ -e $sysfs_cgroup/$cgroup/$attr ]] || return 1 90 91 if [[ -n $val ]]; then 92 echo "$val" > "$sysfs_cgroup/$cgroup/$attr" 93 fi 94} 95 96create_cgroup() { 97 [[ ! -e $sysfs_cgroup/$1 ]] || return 0 98 mkdir "$sysfs_cgroup/$1" 99 if ((cgroup_version == 2)); then 100 echo "threaded" > "$sysfs_cgroup/$1/cgroup.type" 101 fi 102} 103 104remove_cgroup() { 105 local root_cgroup 106 root_cgroup=$(dirname "$1") 107 108 [[ -e $sysfs_cgroup/$1 ]] || return 0 109 move_cgroup_procs "$1" "$root_cgroup" 110 rmdir "$sysfs_cgroup/$1" 111} 112 113exec_in_cgroup() { 114 # Run this function as a background job - the reason why it remains {} instead 115 # of being declared as a subshell is to avoid having an extra bash fork around 116 # - note the exec call. 117 118 local cgroup=$1 119 local proc_interface=cgroup.procs 120 121 shift || return 1 122 123 if ((cgroup_version == 2)) && is_cgroup_threaded "$cgroup"; then 124 proc_interface=cgroup.threads 125 fi 126 set_cgroup_attr "$cgroup" "$proc_interface" "$BASHPID" 127 exec "$@" 128} 129 130kill_in_cgroup() { 131 local cgroup=$1 132 local pid=$2 133 local proc_interface=cgroup.procs 134 local cgroup_pids 135 136 if ((cgroup_version == 2)) && is_cgroup_threaded "$cgroup"; then 137 proc_interface=cgroup.threads 138 fi 139 140 fold_list_onto_array \ 141 cgroup_pids \ 142 $(< "$sysfs_cgroup/$cgroup/$proc_interface") 143 144 if [[ -n $pid ]]; then 145 if [[ -n ${cgroup_pids[pid]} ]]; then 146 kill "$pid" 147 fi 148 elif ((${#cgroup_pids[@]} > 0)); then 149 kill "${cgroup_pids[@]}" 150 fi 151} 152 153remove_cpuset_cgroup() { 154 if ((cgroup_version == 2)); then 155 remove_cgroup /cpuset 156 fi 157} 158 159get_cgroup() { 160 local pid=${1:-self} cgroup 161 162 [[ -e /proc/$pid/cgroup ]] || return 1 163 cgroup=$(< "/proc/$pid/cgroup") 164 echo "${cgroup##*:}" 165} 166 167get_cgroup_path() { 168 local cgroup 169 170 cgroup=$(get_cgroup "$1") || return 1 171 echo "$sysfs_cgroup$cgroup" 172} 173 174_set_cgroup_attr_top_bottom() { 175 local cgroup_path=$1 attr=$2 val=$3 176 177 if [[ -e ${cgroup_path%/*}/$attr ]]; then 178 _set_cgroup_attr_top_bottom "${cgroup_path%/*}" "$attr" "$val" 179 fi 180 181 if [[ -e $cgroup_path/$attr ]]; then 182 echo "$val" > "$cgroup_path/$attr" 183 fi 184} 185 186set_cgroup_attr_top_bottom() { 187 _set_cgroup_attr_top_bottom "$(get_cgroup_path "$1")" "$2" "$3" 188} 189 190declare -r sysfs_cgroup=/sys/fs/cgroup 191cgroup_version=$(check_cgroup) 192