OpenBMC uses systemd to manage all processes. It has its own set of target and service units to control which processes are started. There is a lot of documentation on systemd and to do OpenBMC state work, you're going to have to read some of it. Here's the highlights:
Unit - Units are the basic framework of all systemd work. Service - Services are a type of unit, that define the processes to be run and execute. Target - Targets are another type of unit, they have two purposes:
On an OpenBMC system, you can go to /lib/systemd/system/ and see all of the systemd units on the system. You can easily cat these files to start looking at the relationships among them. Service files can also be found in /etc/systemd/system and /run/systemd/system as well.
systemctl is the main tool you use to interact with systemd and its units.
When an OpenBMC system first has power applied, it starts the "default.target" unless an alternate target is specified on the kernel command line. In Phosphor OpenBMC, there is a link from default.target
to multi-user.target
.
You'll find all the phosphor services associated with multi-user.target
.
When OpenBMC is used within a server, the obmc-host-start@.target is what drives the boot of the system.
To start it you would run systemctl start obmc-host-start@0.target
.
If you dig into its .requires relationship, you'll see the following in the file system
ls -1 /lib/systemd/system/obmc-host-start@0.target.requires/ obmc-host-startmin@0.target phosphor-reset-host-reboot-attempts@0.service
The obmc-host-startmin@.target represents the bare minimum of services and targets required to start the host. This target is also utilized in host reboot scenarios. This distinction of a host-start and a host-startmin target allows the user to put services in the obmc-host-start@.target
that should only be run on an initial host boot (and not run on host reboots). For example, in the output above you can see the user only wants to run the phosphor-reset-host-reboot-attempts@0.service
on a fresh host boot attempt.
Next if we look at the obmc-host-startmin@0.target
, we see this:
ls -1 /lib/systemd/system/obmc-host-startmin@0.target.requires/ obmc-chassis-poweron@0.target start_host@0.service
You can see within obmc-host-startmin@0.target
that we have another target in there, obmc-chassis-poweron@0.target
, along with a service aptly named start_host@0.service
.
The obmc-chassis-poweron@0.target
has corresponding services associated with it:
ls -1 /lib/systemd/system/obmc-chassis-poweron@0.target.requires/ op-power-start@0.service op-wait-power-on@0.service
If you run systemctl start obmc-host-start@0.target
then systemd will start execution of all of the above associated target and services.
The services have dependencies within them that control the execution of each service (for example, the op-power-start.service will run prior to the op-wait-power-on.service). These dependencies are set using targets and the Wants,Before,After keywords.
The soft server power off function is encapsulated in the obmc-host-shutdown@.target
. This target is soft in that it notifies the host of the power off request and gives it a certain amount of time to shut itself down.
The hard server power off is encapsulated in the obmc-chassis-hard-poweroff@.target
. This target will force the stopping of the soft power off service if running, and immediately cut power to the system.
The reboot of the server is encapsulated in the obmc-host-reboot@.target
. This target will utilize the soft power off target and then, once that completes, start the host power on target.
The obmc-host-quiesce@.target
is utilized in host error scenarios. When the obmc-host-quiesce@0.target
is entered, it puts the host state D-Bus [object][https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/xyz/openbmc_project/State/Host.interface.yaml] in a Quiesced
state.
The obmc-chassis-emergency-poweroff@.target
is a wrapper target around the obmc-chassis-hard-poweroff@.target
and obmc-host-quiesce@.target
. It is utilized by applications that have detected critical thermal or power errors which require an immediate shutdown of the system. It will turn the chassis off and put the host into Quiesce (if the host is running). Certain non-critical services in the shutdown path can conflict with this target to ensure only the most critical services are run in this path.
Automated error recovery (i.e. host reboot) will not be done if this target is started. User intervention is required to exit from it. The user could request a power on if they wished or a host stop / power off if they wanted to get out of quiesce.
There are a collection of services within OpenBMC that interact with systemd and its unit files, providing somewhat of an abstraction layer from the user of the OpenBMC system and systemd. See the state interfaces for more information on this function.
For example, if you wanted to execute the server power on function, you would do the following:
busctl set-property xyz.openbmc_project.State.Host /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host RequestedHostTransition s xyz.openbmc_project.State.Host.Transition.On
Underneath the covers, this is calling systemd with the server power on target.
A common question when creating new OpenBMC applications which need to execute some logic in the context of systemd targets is whether they should be triggered by systemd services or by monitoring for the appropriate D-Bus signal indicating the start/stop of the target they are interested in.
The basic guidelines for when to create a systemd service are the following:
With great numbers of targets and services, come great chances for failures. To make OpenBMC a robust and productive system, it needs to be sure to have an error handling policy for when services and their targets fail.
When a failure occurs, the OpenBMC software needs to notify the users of the system and provide mechanisms for either the system to automatically retry the failed operation (i.e. reboot the system) or to stay in a quiesced state so that error data can be collected and the failure can be investigated.
There are two main failure scenarios when it comes to OpenBMC and systemd usage:
Within OpenBMC, there is a host quiesce target. This is the target that other host related targets should go to when they hit a failure. Other software within OpenBMC can then monitor for the entry into this quiesce target and will handle the halt vs. automatic reboot functionality.
Targets which are not host related, will need special thought in regards to their error handling. For example, the target responsible for applying chassis power, obmc-chassis-poweron@0.target
, will have a OnFailure=obmc-chassis-poweroff@%i.target
error path. That is, if the chassis power on target fails then power off the chassis.
The above info sets up some general guidelines for our host related targets and services:
OnFailure=obmc-quiesce-host@.target
OnFailure=obmc-quiesce-host@.target
and ideally set "RemainAfterExit=no" (but see caveats on this below)obmc-quiesce-host@.target
on failuresThis is set to "yes" for most OpenBMC services to handle the situation where someone starts the same target twice. If the associated service with that target is not running (i.e. RemainAfterExit=no), then the service will be executed again. Think about someone accidentally running the obmc-chassis-poweron@.target
twice. If you execute it when the operating system is up and running, and the service which toggles the pgood pin is re-executed, you're going to crash your system. Given this info, the goal should always be to write "oneshot" services that have RemainAfterExit set to yes.
There are some tools available out there to visualize systemd service and target dependencies (systemd-analyze) but due to the complexity of our design, they do not generate anything very useful.
For now, document the current dependencies on a witherspoon system for reference.
R = Requires W = Wants A = After B = Before S = Start (runs a command to start another target or service) (S) = Synchronization Target
obmc-host-shutdown.target R: xyz.openbmc_project.Ipmi.Internal.SoftPowerOff.service W: obmc-host-stopping.target (S) B: obmc-host-stopping.target (S) R: obmc-chassis-poweroff.target R: obmc-host-stop.target R: op-occ-disable.service B: obmc-host-stop-pre.target R: op-power-stop.service W: obmc-power-stop.target (S) B: obmc-power-stop.target (S) W: obmc-power-stop-pre.target (S) A: obmc-power-stop-pre.target (S) W: mapper-wait@-org-openbmc-control-power.service A: mapper-wait@-org-openbmc-control-power.service R: op-wait-power-off.service B: obmc-power-off.target (S) W: obmc-power-stop.target (S) B: obmc-power-stop.target (S) W: obmc-power-stop-pre.target (S) A: obmc-power-stop-pre.target (S) W: mapper-wait@-org-openbmc-control-power.service A: mapper-wait@-org-openbmc-control-power.service R: op-powered-off.service A: op-wait-power-off.service R: op-wait-power-off.service S: obmc-chassis-powered-off.target W: pcie-poweroff.service B: op-power-stop.service A: obmc-power-stop-pre@.target
obmc-power-stop.target W: obmc-power-stop-pre.target A: obmc-power-stop-pre.target obmc-power-stop-pre.target W: obmc-host-stopped.target A: obmc-host-stopped.target obmc-host-stopped.target W: obmc-host-stopping.target A: obmc-host-stopping.target B: obmc-power-stop-pre.target obmc-host-stopping.target W: obmc-host-stop-pre.target A: obmc-host-stop-pre.target B: obmc-host-stopped.target obmc-host-stop-pre.target B: obmc-host-stopping.target