cpi: Introduce new udev rule and integrate with systemd unit

Add a new udev rule that is triggered when a KVM VM is started
on the system the very first time. This rule reports over the
sysfs CPI kernel interface that the system is a KVM host.

Currently the cpi.service systemd unit already works with
the CPI sysfs interface. Introduce a new internal utility
script "cpictl" that is used by both the udev rule and
the systemd unit to guarantee coordinated access to the
kernel CPU interface.

Signed-off-by: Claudio Imbrenda <imbrenda@linux.vnet.ibm.com>
Reviewed-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com>
Reviewed-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
This commit is contained in:
Claudio Imbrenda
2017-09-05 10:49:30 +02:00
committed by Michael Holzheu
parent 8ff6af3a4b
commit ca50924acb
6 changed files with 294 additions and 8 deletions

View File

@@ -35,6 +35,8 @@ Package contents
- 57-osasnmpd.rules: udev rules for osasnmpd.
- 60-readahead.rules: udev rules to set increased "default max readahead".
- 40-z90crypt.rules: udev rules for z90crypt driver
- 90-cpi.rules: udev rule to update Control-Program-Information when KVM is
used.
* systemd units:
- cpi.service: Unit to apply CPI settings

View File

@@ -4,6 +4,11 @@
# The system and sysplex names consist of up to eight characters of
# the following set: A-Z, 0-9, $, @, #, and blank.
#
# CPI system type
#
CPI_SYSTEM_TYPE="LINUX"
#
# CPI system name
#

View File

@@ -0,0 +1,6 @@
#
# Rules to trigger cpictl when KVM is used
# This file should be installed in /etc/udev/rules.d
#
SUBSYSTEM=="misc", KERNEL=="kvm", ENV{CREATED}=="1", ENV{EVENT}=="create", RUN+="/lib/s390-tools/cpictl -b kvm"

View File

@@ -15,7 +15,12 @@ install:
chown $(OWNER):$(GROUP) $(DESTDIR)$(BINDIR)/$$i; \
chmod 755 $(DESTDIR)$(BINDIR)/$$i; \
done
sed -e 's+%S390_TOOLS_VERSION%+$(S390_TOOLS_RELEASE)+' cpictl > \
$(DESTDIR)$(TOOLS_LIBDIR)/cpictl
chown $(OWNER):$(GROUP) $(DESTDIR)$(TOOLS_LIBDIR)/cpictl
chmod 775 $(DESTDIR)$(TOOLS_LIBDIR)/cpictl
@for i in $(MAN_PAGES); \
do \
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 $$i \

274
scripts/cpictl Executable file
View File

@@ -0,0 +1,274 @@
#!/bin/bash
#
# cpictl - Configure the Control-Program-Information (CPI) settings
#
# This is an internal helper script that is used by the "cpi.service"
# systemd unit and the "90-cpi.rules" udev rule.
#
# The bash shell is really needed. Other shells have different ideas of how
# bitwise operators work.
#
# Copyright 2017 IBM Corp.
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
#
readonly CPI_LOCK="/var/lock/cpictl.lock"
readonly PRG="${0##*/}"
readonly SYSTEM_LEVEL_PATH="/sys/firmware/cpi/system_level"
readonly SYSTEM_TYPE_PATH="/sys/firmware/cpi/system_type"
readonly SYSTEM_NAME_PATH="/sys/firmware/cpi/system_name"
readonly SYSPLEX_NAME_PATH="/sys/firmware/cpi/sysplex_name"
readonly CPI_SET="/sys/firmware/cpi/set"
declare LEVEL
declare TYPE
declare NAME
declare SYSPLEX
declare -i DRYRUN=0
# Exit codes
readonly EXIT_SUCCESS=0
readonly EXIT_FAILURE=1
readonly EXIT_ARG_TOO_LONG=3
readonly EXIT_INVALID_CHARS=4
print_help_and_exit()
{
cat <<EndHelp
Usage: $PRG [OPTIONS]
Configure the Control-Program-Information (CPI) settings.
-b, --set-bit BIT Set and commit the bit BIT in the flags
-e, --environment Set and commit the system type, level, name and
sysplex name with values taken from environment
variables
-h, --help Ignore all other options and print this help
-L, --level LEVEL Set and commit OS level to LEVEL (format xx.yy.zzABCD)
-N, --name SYSTEM Set and commit the system name to SYSTEM
-S, --sysplex SYSPLEX Set and commit the sysplex name to SYSPLEX
-T, --type TYPE Set and commit OS type to TYPE
--commit Ignore all other options and commit any uncommitted
values
--dry-run Do not actually set or commit anything, but show what
would be done
--show Ignore all other options and show current (possibly
uncommitted) values
Environment variables used for the --defaults option:
CPI_SYSTEM_TYPE, CPI_SYSTEM_LEVEL, CPI_SYSTEM_NAME, CPI_SYSPLEX_NAME
Available bits for the --set-bit option:
kvm: Indicate that system is a KVM host
The maximum length for system type, system name, and sysplex name is 8
characters. The allowed characters are: [A-Z0-9 @#$]
This tool should be called as root.
EndHelp
exit $EXIT_SUCCESS
}
print_version_and_exit()
{
cat <<EndVersion
$PRG: Configure the CPI settings version %S390_TOOLS_VERSION%
Copyright IBM Corp. 2017
EndVersion
exit $EXIT_SUCCESS
}
cpi_show()
{
cat <<-EndShow
System type: $(cat "$SYSTEM_TYPE_PATH")
System level: $(cat "$SYSTEM_LEVEL_PATH")
System name: $(cat "$SYSTEM_NAME_PATH")
Sysplex name: $(cat "$SYSPLEX_NAME_PATH")
EndShow
}
print_parse_error_and_exit()
{
echo "Try '$PRG --help' for more information." >&2
exit $EXIT_FAILURE
}
fail_with()
{
echo "$1" >&2
echo "Try '$PRG --help' for more information." >&2
exit ${2:-$EXIT_FAILURE}
}
cpi_commit()
{
echo 1 > "$CPI_SET"
}
do_length_check()
{
[ ${#1} -gt 8 ] &&
fail_with "$PRG: Specified $2 too long. The maximum length is 8 characters." $EXIT_ARG_TOO_LONG
}
do_character_check()
{
echo "$1" | grep -q -E '^[a-zA-Z0-9@#$ ]*$' ||
fail_with "$PRG: Invalid characters in $2. Valid characters are: A-Z0-9 @#$" $EXIT_INVALID_CHARS
}
cpi_set_bit()
{
LEVEL=$(printf '0x%x' $((LEVEL | (1 << (63 - $1)) )) )
}
cpi_set_oslevel()
{
local kver=$(echo "${1:-$(uname -r)}" | grep -E -o '^[0-9]+[.][0-9]+[.][0-9]+')
local maj=$((${kver%%.*} % 256))
local min=${kver#*.}
min=$((${min%.*} % 256))
local rev=$((${kver##*.} % 256))
local hexlevel=$(printf '0x%02x%02x%02x' $maj $min $rev)
LEVEL=$(printf '0x%016x' $(((LEVEL & 0xFFFFFFFFFF000000) | hexlevel)))
}
cpi_set_type()
{
TYPE="$1"
do_length_check "$TYPE" "system type"
do_character_check "$TYPE" "system type"
}
cpi_set_sysplex()
{
SYSPLEX="$1"
do_length_check "$SYSPLEX" "sysplex name"
do_character_check "$SYSPLEX" "sysplex name"
}
cpi_set_name()
{
NAME="$1"
do_length_check "$NAME" "system name"
do_character_check "$NAME" "system name"
}
# cpictl starts here
if [ $# -le 0 ]; then
echo "$PRG: No parameters specified"
print_parse_error_and_exit
fi
opts=$(getopt -o b:ehL:N:S:T:v -l set-bit:,environment,help,level:,name:,sysplex:,type:,commit,dry-run,show,version -n $PRG -- "$@")
if [ $? -ne 0 ]; then
print_parse_error_and_exit
fi
# This guarantees that only one instance will be running, and will serialize
# the execution of multiple instances
[ -e "$CPI_LOCK" -a ! -w "$CPI_LOCK" ] &&
fail_with "$PRG: Cannot access lock file: $CPI_LOCK"
[ ! -w "${CPI_LOCK%/*}" ] &&
fail_with "$PRG: Cannot access lock file: $CPI_LOCK"
exec 9<> "$CPI_LOCK"
flock -x 9
# Get current values from sys/firmware
read LEVEL < "$SYSTEM_LEVEL_PATH"
read TYPE < "$SYSTEM_TYPE_PATH"
read NAME < "$SYSTEM_NAME_PATH"
read SYSPLEX < "$SYSPLEX_NAME_PATH"
# Parse command line options: Use eval to remove getopt quotes
eval set -- $opts
while [ -n $1 ]; do
case "$1" in
--help|-h)
print_help_and_exit
;;
--version|-v)
print_version_and_exit
;;
-b|--set-bit)
case "$2" in
kvm)
cpi_set_bit 0
;;
*)
fail_with "$PRG: Unknown bit \"$2\" for the $1 option"
;;
esac
shift 2
;;
-L|--level)
cpi_set_oslevel "$2"
shift 2
;;
-e|--environment)
cpi_set_type "$CPI_SYSTEM_TYPE"
cpi_set_name "$CPI_SYSTEM_NAME"
cpi_set_oslevel "$CPI_SYSTEM_LEVEL"
cpi_set_sysplex "$CPI_SYSPLEX_NAME"
shift
;;
-T|--type)
cpi_set_type "$2"
shift 2
;;
-S|--sysplex)
cpi_set_sysplex "$2"
shift 2
;;
-N|--name)
cpi_set_name "$2"
shift 2
;;
--show)
cpi_show
exit $EXIT_SUCCESS
;;
--commit)
cpi_commit
exit $EXIT_SUCCESS
;;
--dry-run)
DRYRUN=1
shift
;;
--)
shift
break
;;
*)
break;
;;
esac
done
# Print settings for --dry-run or commit them to sysfs otherwise
if [ $DRYRUN -eq 1 ]; then
cat <<-EndDryrun
System type: $TYPE
System level: $LEVEL
System name: $NAME
Sysplex name: $SYSPLEX
EndDryrun
else
echo "$LEVEL" > "$SYSTEM_LEVEL_PATH"
echo "$TYPE" > "$SYSTEM_TYPE_PATH"
echo "$NAME" > "$SYSTEM_NAME_PATH"
echo "$SYSPLEX" > "$SYSPLEX_NAME_PATH"
cpi_commit
fi
exit $EXIT_SUCCESS

View File

@@ -35,12 +35,6 @@ EnvironmentFile=/etc/sysconfig/cpi
# Environment=CPI_SYSTEM_LEVEL=
# Environment=CPI_SYSTEM_TYPE=LINUX
#
# Apply settings
ExecStartPre=/bin/sh -c 'if test -n "$CPI_SYSTEM_TYPE"; then echo $CPI_SYSTEM_TYPE |tr a-z A-Z > /sys/firmware/cpi/system_type; fi'
ExecStartPre=/bin/sh -c 'if test -n "$CPI_SYSTEM_NAME"; then echo $CPI_SYSTEM_NAME |tr a-z A-Z > /sys/firmware/cpi/system_name; fi'
ExecStartPre=/bin/sh -c 'if test -n "$CPI_SYSPLEX_NAME"; then echo $CPI_SYSPLEX_NAME |tr a-z A-Z > /sys/firmware/cpi/sysplex_name; fi'
ExecStartPre=/bin/sh -c 'test -n "$CPI_SYSTEM_LEVEL" || CPI_SYSTEM_LEVEL="$(uname -r)" ; echo $CPI_SYSTEM_LEVEL |cut -d- -f1 | tr . " " |xargs printf "0x0000000000%02x%02x%02x\n" > /sys/firmware/cpi/system_level'
#
# Sending data to the HMC/SE
ExecStart=/bin/sh -c 'echo 1 > /sys/firmware/cpi/set'
ExecStart=@toolslib_path@/cpictl -e