#!/bin/bash # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # This script creates a CPU set and binds the MySQL process to the # set. It restarts MySQL if specified in the option. This script needs # to be run with "sudo". set -e # Exit if any function returns a non-zero value. set -o nounset # Exit if any variable is used unset. MYSQL_PATH='/etc/init.d/mysql.server' MYSQL_PID_PATH='/var/lib/mysql/atlantis1.mtv.corp.google.com.pid' MYSQL_PROC_NAME='mysqld' # The directory where we mount the cpuset virutal file system to. MOUNT_DIR='/dev/cpuset' # The base cpuset directory for mysql. MYSQL_CPUSET='mysql' # CPUs in MySQL cpuset. E.g. 0-2,7,12-14. MYSQL_DEFAULT_CPUS=10-15 # Display usage. function usage() { echo -e "Usage: $0 [-c <CPUs>] [-p <PID>] [-r] [-d]\n" echo -e "Create and bind the MySQL process to a specified CPU set.\n" echo -e "Options:" echo -e " -c <CPUs> Specify a list of CPUs to be used. E.g. 0-2,7,12-14" echo -e " (Default: $MYSQL_DEFAULT_CPUS)." echo -e " -d Delete the CPU set. This option kills the current" echo -e " MySQL process and delete the CPU set. It does not" echo -e " restart MySQL nor create a new CPU set. (Default:" echo -e " disabled)." echo -e " -p <PID> Bind <PID> to the cpuset (Default: the script searches" echo -e " for the MySQL PID automatically)." echo -e " -r Restart MySQL (Default: disabled)." echo -e " -h Display this usage information." echo -e "\n" } function print_info() { msg=$1 echo "INFO: "$msg } function print_error() { msg=$1 echo "ERROR: "$msg } # Run and print out the command if the silent flag is not set (the default). # Usage: run_cmd <cmd> [slient] function run_cmd() { cmd="" slient=0 if [ $# -gt 0 ]; then cmd=$1 else print_error "Empty command!" return 1 fi if [ $# -gt 1 ]; then silent=$2 fi if [ $slient -eq 0 ]; then print_info "Running \"${1}\"" fi # Print an error message if the command failed. eval "$1" || { print_error "Failed to execute \"${cmd}\""; return 1; } } # Get the PID of the MySQL. function get_mysql_pid() { local pid="" if [ $# -gt 0 ] && [ ! -z "$1" ]; then # Use user-provided PID. pid=$1 elif [ ! -z ${MYSQL_PID_PATH} -a -f ${MYSQL_PID_PATH} ]; then # Get PID from MySQL PID file if it is set. print_info "Getting MySQL PID from ${MYSQL_PID_PATH}..." pid=$(cat $MYSQL_PID_PATH) || \ { print_error "No MySQL process found."; return 1; } else # Get PID of process named mysqld. print_info "Searching for MySQL PID..." # Ignore the return code to print out an error message. pid=$(pidof $MYSQL_PROC_NAME) || \ { print_error "No MySQL process found."; return 1; } fi # Test if the PID is an integer if [[ $pid != [0-9]* ]]; then print_error "No MySQL process found." return 1 fi # Check if the PID is a running process. if [ ! -d "/proc/${pid}" ]; then print_error "No running MySQL process is found." return 1 fi _RET="$pid" print_info "MySQL PID is ${pid}." } # Mount the cpuset virtual file system. function mount_cpuset() { if (mount | grep "on ${MOUNT_DIR} type" > /dev/null) then print_info "${MOUNT_DIR} already mounted." else print_info "Mounting cpuset to $MOUNT_DIR." run_cmd "mkdir -p ${MOUNT_DIR}" run_cmd "mount -t cpuset none ${MOUNT_DIR}" fi } function clean_all() { local delete_msg="No" print_info "Will Delete existing CPU set..." echo -ne "WARNING: This operation will kill all running " echo "processes in the CPU set." echo -ne "Are you sure you want to proceed " echo -ne "(type \"yes\" or \"Yes\" to proceed)? " read delete_msg mount_cpuset local proc_list="" local proc="" if [ "$delete_msg" = "yes" -o "$delete_msg" = "Yes" ]; then if [ -d "${MOUNT_DIR}/${MYSQL_CPUSET}" ]; then proc_list=$(cat ${MOUNT_DIR}/${MYSQL_CPUSET}/cgroup.procs) for proc in $proc_list; do run_cmd "kill -9 ${proc}" done # Remove the CPU set directory. run_cmd "rmdir ${MOUNT_DIR}/${MYSQL_CPUSET}" # Unmount the cpuset virtual file system. run_cmd "umount ${MOUNT_DIR}" else print_info "The CPU set does not exist." return 1 fi print_info "Done!" else # User does not wish to continue. print_info "Aborting program." fi } function main() { local MYSQL_CPUS=$MYSQL_DEFAULT_CPUS local RESTART_MYSQL_FLAG=0 local DELETE_CPUSET_FLAG=0 local MYSQL_PID="" # Parse command-line arguments. while getopts ":c:dhp:r" opt; do case $opt in c) MYSQL_CPUS=$OPTARG ;; d) DELETE_CPUSET_FLAG=1 ;; h) usage return 0 ;; p) MYSQL_PID=$OPTARG ;; r) RESTART_MYSQL_FLAG=1 ;; \?) echo "Invalid option: -$OPTARG" >&2 usage return 1 ;; :) echo "Option -$OPTARG requires an argument." >&2 usage return 1 ;; esac done # Clean up and exit if the flag is set. if [ $DELETE_CPUSET_FLAG -eq 1 ]; then clean_all return 0 fi # Restart MySQL. if [ $RESTART_MYSQL_FLAG -eq 1 ]; then print_info "Restarting MySQL..." $MYSQL_PATH restart fi # Get PID of MySQL. get_mysql_pid "$MYSQL_PID" MYSQL_PID=$_RET mount_cpuset # Make directory for MySql. print_info "Making a cpuset for MySQL..." run_cmd "mkdir -p ${MOUNT_DIR}/${MYSQL_CPUSET}" # Change working directory. run_cmd "cd ${MOUNT_DIR}/${MYSQL_CPUSET}" # Update the CPUs to use in the CPU set. Note that we use /bin/echo # explicitly (instead of "echo") because it displays write errors. print_info "Updating CPUs in the cpuset..." run_cmd "bash -c \"/bin/echo ${MYSQL_CPUS} > cpus\"" # Attach/Rebind MySQL process to the cpuset. Note that this command # can only attach one PID at a time. This needs to be run every time # after the CPU set is modified. print_info "Bind MySQL process to the cpuset..." run_cmd "bash -c \"/bin/echo ${MYSQL_PID} > tasks\"" print_info "Done!" } main "$@"