Linux lsvpd design and implementation notes

Martin Schwenke

$Id: design.html,v 1.1 2006/04/11 18:38:28 emunson Exp $


Table of Contents

1. Introduction
2. Requirements
3. Overall architecture
3.1. Data collection
3.2. Querying
4. Database structure
5. Implementation languages
5.1. bash
5.2. C
6. Modular design
6.1. bash
6.1.1. C
6.2. Multiplexed functions
6.2.1. bash
6.2.2. C
7. Legal Statement

1. Introduction

The Linux lsvpd package is a partial reimplementation, for Linux®, of some AIX® hardware inventory and configuration commands. The focus is on providing some of the same serviceability features when running Linux on IBM® POWER® hardware as is provided by AIX. The main function is to list Vital Product Data (VPD) about hardware components, including systems, adapters and devices, in a variety of ways.

This documentation contains design and implementation notes for the Linux lsvpd package. This documentation is aimed at developers who are attempting to update the package. Installation notes are in a different document.

2. Requirements

We are unaware of a requirements document that captures the requirements for all of the commands implemented in the Linux lsvpd package. There may be documents for the requirements of the individual, original AIX commands, but additional requirements will exist for Linux. Therefore, we summarise the requirements for the package here.

Compatibility with AIX commands on IBM POWER systems

Primarily the commands in the lsvpd package must implement the same functionality as their AIX counterparts. In particular, the lsvpd, lsmcode and lsvio commands produce output that is consumed by higher-level software (such as Inventory Scout) - compatibility is very important. lscfg's output is meant for human consumption, so variation and elaboration is not out of the question.

The package should also attempt to list information for the widest variety of hardware possible. Hardware supported by IBM POWER systems should be listed, especially if it is listed by the equivalent AIX commands. Sometimes AIX uses custom device driver hooks to retrieve VPD from particular hardware. This luxury might not be available under Linux so, in this case, the best possible VPD should be (manufactured and) listed.

Cross architecture support

An Open Source package that works on multiple architectures supported by Linux is likely to attract more community attention than a package that works only on IBM POWER systems.

Generic VPD retrieval
For the package to be maintainable it should attempt to use generic methods for retrieving VPD, rather than methods for specific hardware.
Robustness

Since lsvpd is a serviceability package, query commands may be used to investigate a hardware fault. Retrieving VPD from hardware only at the time a service-engineer runs a query command is not a satisfactory solution, since it might not be possible to retrieve VPD from a faulty component. This suggests that an important subset of information should be stored in a database.

Currency

Information in the database should be current. That is, query commands should list information about all the hardware that is currently available/configured.

The Linux lsvpd package currently does a poor job of meeting this requirement. The update-lsvpd-db command currently needs to be run to ensure the database is up-to-date. Hotplug support needs to be implemented. Before this can be done, performance needs to be seriously addressed.

3. Overall architecture

Commands in the package belong to one of two categories: data collection (or scanning) and querying.

3.1. Data collection

Information about hardware is collected and stored in a database under /var/lib/lsvpd/db-DATE-TIME (formerly /var/lib/lsvpd/device-tree, with some supplementary information stored in other subdirectories of /var/lib/lsvpd). There is generally a symbolic link /var/lib/lsvpd/db pointing the the most recent database - so we will use this name to refer to the database in this document. Currently there is only a single data collection command called update-lsvpd-db, which is invoked at boot time and can also be run manually at any time. update-lsvpd-db's main job is to construct linux,vpd directories, containing VPD, for use by the querying commands.

It is assumed that under Linux 2.4 there will be no hotplug events that the lsvpd package needs to know about.

Under Linux 2.6 this will change, so finer grain data collection will need to be implemented. Until proper hotplug support is implemented we have considered implementing a trivial form of hotplug support. This would involve setting a flag when a hotplug event occurs and running update-lsvpd-db if the flag is set and a query command is run with sufficient permissions to update the database. However, this may conflict with the robustness requirement, since information for a faulty component may vanish from the database (due to, say, a retrieval error) if the database is refreshed due to an unrelated hotplug event.

3.2. Querying

The query commands find VPD and other information in the database and present it in a variety of formats. These commands include the following:

lsvpd
Lists VPD. The textual output format is both machine and human readable. This command is used by higher-level system management tools.
lscfg
Lists (hardware) configuration. The textual output format is designed for human consumption. The simplest type of output simply lists a subset of hardware components, and this output can be augmented with VPD. There is also a facility for outputting a platform-specific section.
lsmcode
Lists microcode and firmware levels. Can produce either a tabular, machine-readable format, or a human-oriented format. Doesn't have interactive menus like the AIX version.
lsvio
Lists virtual I/O adapters and devices and their physical locations.
Other
It may be useful to provide additional query interfaces higher-level software. In particular, it might be possible to provide a simple query interface to allow a VPD-based device naming system to be implemented.

4. Database structure

The database under /var/lib/lsvpd/db has two main subdirectories, bus andlinux, as well as two auxiliary subdirectories state and tmp. The bus directory contains a bus view of the system's hardware, while the linux directory contains the operating system's view of adapters, devices and miscellaneous useful information. Devices and adapters are cross linked between the two directories. The state subdirectory is used to store sequence numbers and locks. Supplementary information that used to be stored in (other) subdirectories of /var/lib/lsvpd is now stored in relevant directories of the adapters and devices.

On machines with an Open Firmware device-tree, such as pSeries, the bus directory contains a device-tree subdirectory, which is a copy of /proc/device-tree augmented with linux,vpd nodes (and cross-links).

On other machines with sysfs, the bus directory is populated with information from sysfs and then augmented as above.

5. Implementation languages

5.1. bash

The following observations and requirements influenced the choice of bash as the primary implementation language:

  • A lot of the work done by the lsvpd package is text processing. The shell, along with external commands, is very good at processing text.
  • lsvpd's hardware inventory database might be useful early in the boot sequence as part of a persistent device naming system or similar. The only scripting language that is generally available on a Linux root filesystem is the shell.
  • /bin/bash is the usual Linux shell, and contains useful features, such as built-in arithmetic operations and a certain amount of string handling. It also has many commonly used commands built-in.

5.2. C

Helper utilities that can't be implemented in bash, and are not otherwise available, are implemented in C. This allows small, efficient executables to be produced, which is appropriate for certain early-boot environments, such as in an initramfs.

At this point in time, much of the functionality is being migrated to larger C programs. This is to allow lsvpd to realistically cope with hotplug. Eventually the whole package will be rewritten in C.

6. Modular design

6.1. bash

The main commands in the lsvpd package, written in bash, are written in a modular manner. This allows their complexity to be more easily managed and allows features to be added depending on (possibly architecture-dependent) operating system features and availability of other commands or packages.

There are several subdirectories of modules:

common.d
Contains modules available to all commands.
scan.d
Contains modules used during data collection, currently just the update-lsvpd-db command.
query.d
Contains modules used by all of the query commands.
lsvpd.d, lscfg.d, lsmcode.d
Each of these directories contains modules used by the associated query command.
common-post.d
Contains modules available to all commands, but is loaded after other modules.

At the simplest level, modules work by redefining shell functions that are defined in previously loaded modules. Default modules contain stubs or versions of functions with minimal functionality. Modules that are loaded later may then define more sophisticated versions of those functions, depending on the available functionality.

6.1.1. C

The C reimplementation uses a similar system for deciding whether to use functions from a particular module at run-time. Each module uses features of src/init.h to define an init function that overrides relevant function pointers, perhaps if an optional condition is met. The INIT macro is used to make the init function available early in the initialisation phase. The inline function call_inits, which is called from main, calls the init function from each module in the order the modules were passed to the linker (ld). In this way, the logic detailing the relative priorities of the modules is needed in only one place: the Makefile.

6.2. Multiplexed functions

6.2.1. bash

To avoid a plethora of case statements, a function multiplexing scheme has been implemented. A multiplexed function is declared like this:

	    make_multiplexed add_device
	  

Initially, this causes any calls to this function, such as

	    add_device scsi 0:0:0:1
	  

to fail silently, since the add_device function has been declared but not defined.

Subsequent modules can then define individual implementation functions that are called according to the first argument that is passed to add_device. For example, if an implementation called add_device_scsi is defined, it will be called when the first argument is scsi, as in the above example. If the first argument does not coincide with a defined implementation, then the implementation add_device_DEFAULT will be called, if it is defined. Otherwise silent failure occurs. This allows modules to incrementally introduce functionality for different device types.

Note that if a module wishes to replace a number of implementations with a ..._DEFAULT implementation, it will also need to use set -f to remove unwanted implementations. Alternatively, if a module is able to provide all implementations using a single function, then it can simply define a new add_device function and completely override the multiplexing.

6.2.2. C

The C implementation uses a similar scheme for function multiplexing. One of the main differences between this implementation and the bash one is that all functions require a type argument, since C is strongly typed (at least compare to bash).

This has currently been partially implemented for devices. The current components of the implementation work like this:

  • src/device.h contains a typedef for each function type.
  • src/device.h contains a member in struct device_functions for each function. This structure also has a member that indicates the type of functions in the structure (default, SCSI, IDE).
  • src/device.h contains a declaration for each top-level function.
  • src/device.c contains a definition for each top-level function, using the macros DEVICE_MULTIPLEXED_VOID and DEVICE_MULTIPLEXED_RET.
  • Modules assign functions into one or more device_functions structures (with a relevant type flag) and register them using device_type_register. Possibly previously defined functions can be undefined by assigning the macro DEVICE_UNDEF to a function member.

7. Legal Statement

  • This work represents the view of the author and does not necessarily represent the view of IBM.
  • Linux is a registered trademark of Linus Torvalds.
  • IBM, AIX, pSeries and POWER are trademarks or registered trademarks of International Business Machines Corporation in the United States and/or other countries.
  • Other company, product, and service names may be trademarks or service marks of others.