From ca50924acb890daafe43b08437a5bd64da695ec5 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 5 Sep 2017 10:49:30 +0200 Subject: [PATCH] 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 Reviewed-by: Viktor Mihajlovski Reviewed-by: Michael Holzheu Reviewed-by: Hendrik Brueckner Signed-off-by: Michael Holzheu --- README.md | 2 + etc/sysconfig/cpi | 5 + etc/udev/rules.d/90-cpi.rules | 6 + scripts/Makefile | 7 +- scripts/cpictl | 274 ++++++++++++++++++++++++++++++++++ systemd/cpi.service.in | 8 +- 6 files changed, 294 insertions(+), 8 deletions(-) create mode 100644 etc/udev/rules.d/90-cpi.rules create mode 100755 scripts/cpictl diff --git a/README.md b/README.md index 37b26e2c..469a3fd9 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/etc/sysconfig/cpi b/etc/sysconfig/cpi index e2c925bc..866b5895 100644 --- a/etc/sysconfig/cpi +++ b/etc/sysconfig/cpi @@ -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 # diff --git a/etc/udev/rules.d/90-cpi.rules b/etc/udev/rules.d/90-cpi.rules new file mode 100644 index 00000000..b7cb3e2a --- /dev/null +++ b/etc/udev/rules.d/90-cpi.rules @@ -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" diff --git a/scripts/Makefile b/scripts/Makefile index 94cd6e02..f0c2ff7a 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -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 \ diff --git a/scripts/cpictl b/scripts/cpictl new file mode 100755 index 00000000..f784f835 --- /dev/null +++ b/scripts/cpictl @@ -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 <&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 diff --git a/systemd/cpi.service.in b/systemd/cpi.service.in index 83624877..b6357d68 100644 --- a/systemd/cpi.service.in +++ b/systemd/cpi.service.in @@ -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