1 #!/bin/bash 2 3 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 4 # Use of this source code is governed by a BSD-style license that can be 5 # found in the LICENSE file. 6 7 # This script creates a CPU set and binds the MySQL process to the 8 # set. It restarts MySQL if specified in the option. This script needs 9 # to be run with "sudo". 10 11 set -e # Exit if any function returns a non-zero value. 12 set -o nounset # Exit if any variable is used unset. 13 14 MYSQL_PATH='/etc/init.d/mysql.server' 15 MYSQL_PID_PATH='/var/lib/mysql/atlantis1.mtv.corp.google.com.pid' 16 MYSQL_PROC_NAME='mysqld' 17 18 # The directory where we mount the cpuset virutal file system to. 19 MOUNT_DIR='/dev/cpuset' 20 # The base cpuset directory for mysql. 21 MYSQL_CPUSET='mysql' 22 # CPUs in MySQL cpuset. E.g. 0-2,7,12-14. 23 MYSQL_DEFAULT_CPUS=10-15 24 25 26 # Display usage. 27 function usage() { 28 echo -e "Usage: $0 [-c <CPUs>] [-p <PID>] [-r] [-d]\n" 29 echo -e "Create and bind the MySQL process to a specified CPU set.\n" 30 echo -e "Options:" 31 echo -e " -c <CPUs> Specify a list of CPUs to be used. E.g. 0-2,7,12-14" 32 echo -e " (Default: $MYSQL_DEFAULT_CPUS)." 33 echo -e " -d Delete the CPU set. This option kills the current" 34 echo -e " MySQL process and delete the CPU set. It does not" 35 echo -e " restart MySQL nor create a new CPU set. (Default:" 36 echo -e " disabled)." 37 echo -e " -p <PID> Bind <PID> to the cpuset (Default: the script searches" 38 echo -e " for the MySQL PID automatically)." 39 echo -e " -r Restart MySQL (Default: disabled)." 40 echo -e " -h Display this usage information." 41 echo -e "\n" 42 } 43 44 function print_info() { 45 msg=$1 46 echo "INFO: "$msg 47 } 48 49 function print_error() { 50 msg=$1 51 echo "ERROR: "$msg 52 } 53 54 # Run and print out the command if the silent flag is not set (the default). 55 # Usage: run_cmd <cmd> [slient] 56 function run_cmd() { 57 cmd="" 58 slient=0 59 60 if [ $# -gt 0 ]; then 61 cmd=$1 62 else 63 print_error "Empty command!" 64 return 1 65 fi 66 67 if [ $# -gt 1 ]; then 68 silent=$2 69 fi 70 71 if [ $slient -eq 0 ]; then 72 print_info "Running \"${1}\"" 73 fi 74 75 # Print an error message if the command failed. 76 eval "$1" || { print_error "Failed to execute \"${cmd}\""; return 1; } 77 } 78 79 # Get the PID of the MySQL. 80 function get_mysql_pid() { 81 local pid="" 82 83 if [ $# -gt 0 ] && [ ! -z "$1" ]; then 84 # Use user-provided PID. 85 pid=$1 86 elif [ ! -z ${MYSQL_PID_PATH} -a -f ${MYSQL_PID_PATH} ]; then 87 # Get PID from MySQL PID file if it is set. 88 print_info "Getting MySQL PID from ${MYSQL_PID_PATH}..." 89 pid=$(cat $MYSQL_PID_PATH) || \ 90 { print_error "No MySQL process found."; return 1; } 91 else 92 # Get PID of process named mysqld. 93 print_info "Searching for MySQL PID..." 94 # Ignore the return code to print out an error message. 95 pid=$(pidof $MYSQL_PROC_NAME) || \ 96 { print_error "No MySQL process found."; return 1; } 97 fi 98 99 # Test if the PID is an integer 100 if [[ $pid != [0-9]* ]]; then 101 print_error "No MySQL process found." 102 return 1 103 fi 104 105 # Check if the PID is a running process. 106 if [ ! -d "/proc/${pid}" ]; then 107 print_error "No running MySQL process is found." 108 return 1 109 fi 110 111 _RET="$pid" 112 print_info "MySQL PID is ${pid}." 113 } 114 115 # Mount the cpuset virtual file system. 116 function mount_cpuset() { 117 if (mount | grep "on ${MOUNT_DIR} type" > /dev/null) 118 then 119 print_info "${MOUNT_DIR} already mounted." 120 else 121 print_info "Mounting cpuset to $MOUNT_DIR." 122 run_cmd "mkdir -p ${MOUNT_DIR}" 123 run_cmd "mount -t cpuset none ${MOUNT_DIR}" 124 fi 125 } 126 127 128 function clean_all() { 129 local delete_msg="No" 130 print_info "Will Delete existing CPU set..." 131 echo -ne "WARNING: This operation will kill all running " 132 echo "processes in the CPU set." 133 echo -ne "Are you sure you want to proceed " 134 echo -ne "(type \"yes\" or \"Yes\" to proceed)? " 135 read delete_msg 136 137 mount_cpuset 138 139 local proc_list="" 140 local proc="" 141 142 if [ "$delete_msg" = "yes" -o "$delete_msg" = "Yes" ]; then 143 if [ -d "${MOUNT_DIR}/${MYSQL_CPUSET}" ]; then 144 proc_list=$(cat ${MOUNT_DIR}/${MYSQL_CPUSET}/cgroup.procs) 145 for proc in $proc_list; do 146 run_cmd "kill -9 ${proc}" 147 done 148 # Remove the CPU set directory. 149 run_cmd "rmdir ${MOUNT_DIR}/${MYSQL_CPUSET}" 150 # Unmount the cpuset virtual file system. 151 run_cmd "umount ${MOUNT_DIR}" 152 else 153 print_info "The CPU set does not exist." 154 return 1 155 fi 156 print_info "Done!" 157 else 158 # User does not wish to continue. 159 print_info "Aborting program." 160 fi 161 } 162 163 164 function main() { 165 166 local MYSQL_CPUS=$MYSQL_DEFAULT_CPUS 167 local RESTART_MYSQL_FLAG=0 168 local DELETE_CPUSET_FLAG=0 169 local MYSQL_PID="" 170 171 # Parse command-line arguments. 172 while getopts ":c:dhp:r" opt; do 173 case $opt in 174 c) 175 MYSQL_CPUS=$OPTARG 176 ;; 177 d) 178 DELETE_CPUSET_FLAG=1 179 ;; 180 h) 181 usage 182 return 0 183 ;; 184 p) 185 MYSQL_PID=$OPTARG 186 ;; 187 r) 188 RESTART_MYSQL_FLAG=1 189 ;; 190 \?) 191 echo "Invalid option: -$OPTARG" >&2 192 usage 193 return 1 194 ;; 195 :) 196 echo "Option -$OPTARG requires an argument." >&2 197 usage 198 return 1 199 ;; 200 esac 201 done 202 203 204 # Clean up and exit if the flag is set. 205 if [ $DELETE_CPUSET_FLAG -eq 1 ]; then 206 clean_all 207 return 0 208 fi 209 210 # Restart MySQL. 211 if [ $RESTART_MYSQL_FLAG -eq 1 ]; then 212 print_info "Restarting MySQL..." 213 $MYSQL_PATH restart 214 fi 215 216 217 # Get PID of MySQL. 218 get_mysql_pid "$MYSQL_PID" 219 MYSQL_PID=$_RET 220 221 mount_cpuset 222 223 # Make directory for MySql. 224 print_info "Making a cpuset for MySQL..." 225 run_cmd "mkdir -p ${MOUNT_DIR}/${MYSQL_CPUSET}" 226 227 # Change working directory. 228 run_cmd "cd ${MOUNT_DIR}/${MYSQL_CPUSET}" 229 230 # Update the CPUs to use in the CPU set. Note that we use /bin/echo 231 # explicitly (instead of "echo") because it displays write errors. 232 print_info "Updating CPUs in the cpuset..." 233 run_cmd "bash -c \"/bin/echo ${MYSQL_CPUS} > cpus\"" 234 235 # Attach/Rebind MySQL process to the cpuset. Note that this command 236 # can only attach one PID at a time. This needs to be run every time 237 # after the CPU set is modified. 238 print_info "Bind MySQL process to the cpuset..." 239 run_cmd "bash -c \"/bin/echo ${MYSQL_PID} > tasks\"" 240 241 print_info "Done!" 242 } 243 244 main "$@" 245