TCP-Verbindungsabbrüche wegen „max assembly queue depth“

Kürzlich hatten wir häufige Programm-Abbrüche von einigen Java-Client Programmen. Im Java Stack-Trace war folgendes zu finden:

[STACK] Caused by: java.io.IOException: Premature EOF
[STACK]           at sun.net.www.http.ChunkedInputStream.readAheadBlocking(Unknown Source)
[STACK]           at sun.net.www.http.ChunkedInputStream.readAhead(Unknown Source)
[STACK]           at sun.net.www.http.ChunkedInputStream.read(Unknown Source)
[STACK]           at java.io.FilterInputStream.read(Unknown Source)

Das Problem tritt in der Klasse ChunkedInputStream in der Methode readAheadBlocking auf. Im Source Code der Methode findet man:

558 /**
559 * If we hit EOF it means there's a problem as we should never
560 * attempt to read once the last chunk and trailers have been
561 * received.
562 */
563 if (nread < 0) {
564 error = true;
565 throw new IOException("Premature EOF");
566 }

Der Wert nread wird kleiner 0, wenn das Ende des Datenstroms erreicht ist. Dies kann auch passieren wenn die Gegenseite die Verbindung unerwartet schließt.

Die Serverseite war in diesem Fall ein AIX-System (AIX 7.1 TL5 SP3). Eine Überprüfung der TCP-Verbindungen auf Abbrüche (Drops) mittels netstat ergab:

$ netstat -p tcp | grep drop
        361936 connections closed (including 41720 drops)
        74718 embryonic connections dropped
                0 connections dropped by rexmit timeout
                0 connections dropped due to persist timeout
                0 connections dropped by keepalive
        0 packets dropped due to memory allocation failure
        0 Connections dropped due to bad ACKs
        0 Connections dropped due to duplicate SYN packets
        1438 connections dropped due to max assembly queue depth
$

Demnach gab es 1438 Verbindungsabbrüche wegen Erreichen der maximalen TCP Assembly Queue Tiefe (max assembly queue depth). Die Queue Tiefe wird über den neuen Kernel Parameter tcp_maxqueuelen konfiguriert, der als Fix für den CVE-2018-6922 eingeführt wurde (siehe: The CVE-2018-6922 fix (FreeBSD vulnerability) and scp) . Der Defaultwert ist 1000. Bei größeren Paket-Laufzeiten kann es zum Überlauf der Queue kommen.

Nach einer Erhöhung des Kernel-Parameters tcp_maxqueuelen sind keine Verbindungsabbrüche mehr aufgetreten.

ProbeVue in Action: Überwachen der „Queue Depth“ von Platten

Platten und Storage Systeme unterstützen Tagged Command Queueing, d.h. angeschlossene Server können mehrere I/O Aufträge an die Platte oder das Storage-System senden ohne zu Warten das ältere I/O-Aufträge fertig sind. Wieviele I/O-Aufträge man an eine Platte senden darf, bevor man warten muss das ältere I/O-Aufträge abgeschlossen wurden, kann über das hdisk Attribut queue_depth unter AIX konfiguriert werden. Für viele hdisk Typen ist der Wert 20 für die queue_depth der Default-Wert. In der Regel erlauben die meisten Storage-Systeme aber noch größere Werte für die Queue-Depth.

Mit Hilfe von ProbeVue lässt sich die Auslastung der Platten-Queue sehr leicht beobachten.

Ab AIX 7.1 TL4 bzw. AIX 7.2 TL0 unterstützt AIX den I/O Probe Manager. Damit lassen sich auf einfache Weise Ereignisse im I/O Stack von AIX tracen. Wird ein I/O vom Platten-Treiber gestartet, so geschieht dies über die Funktion iostart im Kernel, der Request wird an den Adapter-Treiber weitergegeben und geht dann über den Host-Bus-Adapter an das Storage-System. Das Bearbeiten der Antwort wird von der Funktion iodone im Kernel übernommen. Der I/O Probe-Manager unterstützt (unter anderem) Proben an diesen Stellen:

@@io:disk:iostart:read:<filter>
@@io::disk:iostart:write:<filter>
@@io:disk:iodone:read:<filter>
@@io::disk:iodone:write:<filter>

Als Filter kann z.B. ein Hdisk Name wie hdisk2 angegeben werden. Die Proben-Punkte lösen dann nur Ereignisse für die Platte hdisk2 aus. Damit lässt sich schon einmal eine Aktion durchführen wann immer ein I/O für eine Hdisk beginnt oder endet. Damit könnte man z.B. messen wie lange eine I/O Operation dauert oder auch einfach nur mitzählen wieviele I/Os ausgeführt werden. In unserem Beispiel waren wir aber an der Auslastung der Platten-Queue interessiert, d.h. der Anzahl I/Os die an die Platte gesendet aber noch nicht abgeschlossen wurden. Der I/O Probe-Manager besitzt für die I/O Ereignisse  iostart und iodone die Builtin-Variable __diskinfo mit den folgenden Feldern (https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/com.ibm.aix.genprogc/probevue_man_io.htm):

name          char*     Name der Platte
…
queue_depth   int       Die Queue-Depth der Platte (Wert aus der ODM)
cmds_out      int       Anzahl der ausstehenden I/Os
…

Das Feld cmds_out gibt an wieviele I/Os an die Platte gesendet wurden, für die das I/O noch nicht abgeschlossen ist (Antwort ist noch nicht beim Server angekommen).

Mit dem folgenden Code-Abschnitt ermitteln wir die minimale, maximale und durchschnittliche Anzahl an Einträgen in der Platten-Queue:

@@io:disk:iostart:*:hdisk0     // Nur I/Os für hdisk0 berücksichtigen
{
   queue = __iopath->cmds_out; // Anzahl ausstehende I/Os in Variable queue festhalten
   ++numIO;                    // Anzahl I/Os in der Variablen numIO mitzählen (wegen Durchschnittsbildung)
   avg += queue;               // Variable avg um Anzahl ausstehende I/Os erhöhen
   if ( queue < min )
      min = queue;             // Überprüfen auf Minimum und gegebenenfalls setzen
   if ( queue > max )
      max = queue;             // Überprüfen auf Maximum und gegebenenfalls setzen
}

Die ermittelten Werte geben wir dann einmal pro Sekunde mit Hilfe des Intervall Probe-Managers aus:

@@interval:*:clock:1000
{
   if ( numIO == 0 )
      numIO = 1;    // Verhindert Division durch 0 bei der Durchschnittsbildung
   if ( min > max )
      min = max;
   printf( "%5d  %5d  %5d\n" , min , avg/numIO , max );
   min = 100000;   // Zurücksetzen der Variablen für das nächste Intervall
   avg = 0;
   max = 0;
   numIO = 0;
}

Das vollständige Skript ist auf unserer Webseite zum Download verfügbar: ioqueue.e.

Hier ein Beispiel-Lauf des Skriptes für die Platte hdisk13:

# ./ioqueue.e hdisk13
  min    avg    max
    1      1      2
    1      1      9
    1      1      2
    1      1      8
    1      1      2
    1      1      2
    1      1      8
    1      1     10
    1      1      2
    1      1      1
    1      1     10
    1      1      2
    1      1     11
...

Das Skript erwartet die Angabe einer hdisk als Argument und gibt dann einmal pro Sekunde die ermittelten Werte für die angegebene hdisk aus.

In der Beispiel-Ausgabe sieht man das die maximale Anzahl der Einträge in der Platten-Queue 11 ist. Eine Erhöhung des Attributes queue_depth macht daher aus Performance-Sicht keinen Sinn.

Hier ein anderes Beispiel:

# ./ioqueue.e hdisk21
  min    avg    max
    9     15     20
   11     17     20
   15     19     20
   13     19     20
   14     19     20
   17     18     20
   18     18     19
   16     19     20
   13     18     20
   18     19     19
   17     19     20
   18     19     20
   17     19     19
...

In diesem Fall wird der maximale Wert 20 (die hdisk21 hat eine queue_depth von 20) regelmäßig erreicht. Eine Erhöhung der queue_depth kann in diesem Fall zu einer Verbesserung des Durchsatzes führen.

Das Beispiel-Skript lässt sich natürlich noch beliebig erweitern, man könnte z.B. noch den Durchsatz erfassen, oder die Wartezeit von I/Os in der Wait-Queue oder auch die Position und Größe jedes I/Os auf der Platte. Das dargestellte Beispiel zeigt wie einfach man Informationen zu I/Os mit Hilfe von ProbeVue ermitteln kann.

 

Volles Filesystem: df und du zeigen unterschiedliche Belegung

Volle Filesysteme kommen in der Praxis immer wieder vor, jeder kennt dies. Üblicherweise sucht man dann nach großen Dateien oder Verzeichnissen und überprüft ob ältere Daten gelöschte werden können um wieder Platz zu schaffen (manchmal wird aber auch einfach das Filesystem vergrößert ohne genauere Untersuchung). In manchen Fällen lassen sich aber keine größeren Dateien finden die man löschen könnte oder man entdeckt das scheinbar Filesystem-Platz weg ist, kann aber nicht identifizieren wo dieser Platz verwendet wird. Das Kommando du zeigt dann einen kleineren Wert für den verwendeten Filesystem-Platz an als df. Im folgenden ist ein solches Beispiel gezeigt, und auch der Hinweis wie sich identifizieren lässt wo der Filesystem-Platz abgeblieben ist und wie er sich dann letztlich auch wiedergewinnen lässt. AIX hat hier eine schöne Möglichkeit zu bieten, die man nicht in jedem UNIX-Derivat findet.

Das Filesystem /var/adm/log ist zu 91% gefüllt, aktuell sind 3.6 GB des Filesystems in Benutzung:

# df -g  /var/adm/log
Filesystem    GB blocks      Free %Used    Iused %Iused Mounted on
/dev/varadmloglv      4.00      0.39   91%      456     1% /var/adm/log
#

Eine Überprüfung mit dem Kommando du zeigt das scheinbar wesentlich weniger Platz belegt ist:

# du –sm /var/adm/log
950.21   /var/adm/log
#

Das Kommando “disk usage” kommt lediglich auf 950 MB belegten Platz! Das sind 2.7 GB weniger als der Wert aus dem Kommando df. Doch wo ist der fehlende Platz?

Der Unterschied kommt von Dateien die gelöscht wurden, aber noch von mindestens einem Prozeß geöffnet sind. Der Eintrag für solche Dateien wird aus dem zugehörigen Directory entfernt, womit auf die Datei nicht mehr zugegriffen werden kann. Daher zählt das Kommando du diese Dateien bei der Aufsummierung auch nicht mit und kommt auf den kleineren Wert. Solange ein Prozeß die gelöschte Datei noch in Benutzung hat, werden die zugehörigen Blöcke im Filesystem aber nicht freigegeben, daher zeigt df diese auch als belegt an.

Es gibt also mindestens eine Datei in dem Filesystem /var/adm/log welche gelöscht wurde, aber noch von einem Prozeß geöffnet ist. Es stellt sich die Frage wie man den betreffenden Prozeß und die Datei identifizieren kann.

AIX bietet eine einfache Möglichkeit Prozesse zu identifizieren die gelöschte Dateien geöffnet haben, das Kommando fuser besitzt hierfür die Option -d um Prozesse aufzulisten, die gelöschte Dateien geöffnet haben:

# fuser -d /var/adm/log
/var/adm/log:  9110638
#

Verwendet man zusätzlich die Option –V, dann werden auch noch Informationen zu den gelöschten Dateien angezeigt, wie Inode-Nummer und Dateigröße:

# fuser -dV /var/adm/log
/var/adm/log:
inode=119    size=2882647606   fd=12     9110638
#

Die Ausgabe zeigt das hier die Datei mit der Inode-Nummer 119 mit der Größe ca 2.8 GB gelöscht wurde, aber vom Prozeß mit der PID 9110638 über den File Descriptor 12 immer noch geöffnet ist.

Mittels ps lässt sich schnell herausfinden um welchen Prozeß es sich handelt:

# ps -ef|grep 9110638
    root  9110638  1770180   0   Nov 20      - 28:28 /usr/sbin/syslogd
    root  8193550  8849130   0 09:13:35  pts/2  0:00 grep 9110638
#

Es handelt sich hier um den syslogd. Vermutlich wurde hier eine Log-Datei mittels mv rotiert, ohne den syslogd zu informieren (refresh –s syslogd). Wir holen dies kurz nach und überprüfen dann noch einmal das Filesystem:

# refresh -s syslogd
0513-095 The request for subsystem refresh was completed successfully.
#
# df -g /var/adm/log
Filesystem    GB blocks      Free %Used    Iused %Iused Mounted on
/dev/varadmloglv      4.00      3.07   24%      455     1% /var/adm/log
#

Die Ausgabe zeigt das die Filesystem-Blöcke jetzt freigegeben wurden.

 

ProbeVue in Action: Identifizieren eines Abstürzenden Prozesses

Kürzlich meldete unser Monitoring ein volles /var-Filesystem auf einem unserer Systeme. Schnell war herausgefunden das Core-Files im Verzeichnis /var/adm/core das Filesystem gefüllt hatten. Es stellte sich schnell heraus das alle Core-Files von Perl stammten. Allerdings konnte anhand der Core-Files nicht festgestellt werden welches Perl-Skript den Absturz von Perl verursacht hatte. Ein Blick auf die Zeitstempel der Core-Files ließ leider kein Muster erkennen:

-bash-4.4$ ls -ltr /var/adm/core
total 2130240
drwxr-xr-x    2 root     system          256 Jan 29 10:20 lost+found/
-rw-------    1 root     system    100137039 Jun 26 04:51 core.22610328.26025105.Z
-rw-------    1 root     system     99054991 Jun 26 06:21 core.21102892.26042104.Z
-rw-------    1 root     system     99068916 Jun 26 08:06 core.18153840.26060607.Z
-rw-------    1 root     system    100132866 Jun 26 08:21 core.19005848.26062105.Z
-rw-------    1 root     system     97986020 Jun 26 16:36 core.15270246.26143608.Z
-rw-------    1 root     system     99208958 Jun 26 22:21 core.22675838.26202106.Z
-rw-------    1 root     system     97557063 Jun 27 01:06 core.5505292.26230604.Z
-rw-------    1 root     system     98962499 Jun 27 10:06 core.8257960.27080603.Z
-rw-------    1 root     system     99804173 Jun 27 14:51 core.18940202.27125107.Z
-rw-------    1 root     system     99633676 Jun 28 03:21 core.17563960.28012107.Z
-rw-------    1 root     system     99116032 Jun 28 19:06 core.8651210.28170608.Z
-bash-4.4$

Auch die Einträge im Error Report lieferten keine Informationen um welches Perl-Skript es sich hier handelt und auf welchem Wege dieses gestartet wurde.

 

-bash-4.4$ sudo errpt -j A924A5FC –a
...
---------------------------------------------------------------------------
LABEL:          CORE_DUMP
IDENTIFIER:     A924A5FC

Date/Time:       Wed May 29 15:21:25 CEST 2019
Sequence Number: 17548
Machine Id:      XXXXXXXXXXXX
Node Id:         XXXXXXXX
Class:           S
Type:            PERM
WPAR:            Global
Resource Name:   SYSPROC        

Description
SOFTWARE PROGRAM ABNORMALLY TERMINATED

Probable Causes
SOFTWARE PROGRAM

User Causes
USER GENERATED SIGNAL

        Recommended Actions
        CORRECT THEN RETRY

Failure Causes
SOFTWARE PROGRAM

        Recommended Actions
        RERUN THE APPLICATION PROGRAM
        IF PROBLEM PERSISTS THEN DO THE FOLLOWING
        CONTACT APPROPRIATE SERVICE REPRESENTATIVE

Detail Data
SIGNAL NUMBER
         11
USER'S PROCESS ID:
              13369662
FILE SYSTEM SERIAL NUMBER
           1
INODE NUMBER
                 69639
CORE FILE NAME
/var/adm/core/core.13369662.29132106
PROGRAM NAME
perl
STACK EXECUTION DISABLED
           0
COME FROM ADDRESS REGISTER

PROCESSOR ID
  hw_fru_id: 1
  hw_cpu_id: 19

ADDITIONAL INFORMATION

Unable to generate symptom string.
Too many stack elements.
-bash-4.4$

Die einzige Information, die sich entnehmen ließ, war das die Prozesse mit dem Signal 11 (SIGSEGV) beendet wurden, also aufgrund eines Zugriffs auf eine ungültige Speicher-Adresse.

Es stellte sich die Frage: wie kann ermittelt werden um welches Perl-Skript es sich handelt und auf welchem Wege es gestartet wird.

Dies sollte sich eigentlich mit Hilfe von ProbeVue herausfinden lassen.

Es bot sich der sysproc-Provider an, der im Falle eines Exits eines Prozesses ein Event generiert. Über die spezielle built-in Variable __exitinfo werden genauere Informationen zum Exit, wie Exit-Status oder die Signal-Nummer die den Prozess beendet hat bereitgestellt. Damit lässt sich schon einmal die folgende Probe schreiben:

1: @@sysproc:exit:*
2: when ( __exitinfo->signo == 11 )
3: {
4:         printf( "%llu:  %s\n" , __pid , __pname );
5:         ptree(10);
6: }

Die 6 Zeilen seien hier kurz erklärt:

  1. Der Probe-Point: Provider ist sysproc, Event is exit, * bedeutet beliebiger Prozeß
  2. Durch Verwendung des obigen Prädikats wird der nachfolgende Action Block nur ausgeführt, wenn der Prozeß mit dem Signal 11 (SIGSEGV) beendet wurde.
  3. Start des Action Blocks.
  4. Ausgeben der PID und des Programm-Namens des Prozesses.
  5. Die Funktion ptree gibt den Vater, Großvater usw. (bis 10 Ebenen) des Prozesses aus.
  6. Hier endet der Action Block.

Leider lassen sich hier keine Argumente auflisten, mit denen das Programm gestartet wurde, was in unserem Falle mit Perl den Namen des Skriptes geliefert hätte. Aber immerhin bekommt man über die Funktion ptree heraus auf welchem Wege das Programm aufgerufen wurde, was in manchen Fällen schon ausreicht um das Programm dann letztlich zu identifizieren.

Wir hätten zur Identifikation gerne noch die Information über die Argumente mit denen Perl aufgerufen wurde. Diese Information liefert der syscall-Provider für den System-Call execve mit dem das Programm letztlich gestartet wird. Der Probe-Point ist damit syscall:*:execve:entry, da beim Eintritt in die Funktion die Argumente bekannt sind. Die Signatur von execve für ProbeVue sieht dann so aus:

int execve( char* , struct arg_t* args , char* );

Hierbei ist das erste Argument (wird von ProbeVue als __arg1 bereitgestellt) der Programm-Name. Das zweite Argument ist eine Struktur mit den gesuchten Argumenten (bereitgestellt über __arg2). Über das dritte Argumente hat man Zugriff auf Environment-Variablen, was aber in unserem Falle nicht von Bedeutung ist. Die Struktur struct arg_t sieht für 5 Argumente so aus:

struct arg_t
{
        union
        {
                char* arg[5];
                int num[5];
        } u;
};

Diese Struktur und die Signatur von execve müssen im ProbeVue-Skript deklariert werden, bevor man diese benutzen kann.

Beim Zugriff auf die Argumente ergibt sich dann noch ein kleines weiteres Problem: wenn der Action Block für unsere Probe angesprochen wird, sind wir im Kernel-Modus, die Argumente selber sind aber Adressen im User-Mode des Prozesses. Die Daten (in diesem Falle Zeichenketten) müssen aus dem User-Adreß-Raum herauskopiert werden. Dies erledigt die Funktion get_userstring.

Wir lassen uns bei jedem execve die PID, den Programm-Namen, das Kommando und bis zu 5 Argumente ausgeben. Dies ist im folgenden Programm implementiert:

#! /usr/bin/probevue

struct arg_t
{
        union
        {
                char* arg[5];
                int num[5];
        } u;
};

int execve( char* , struct arg_t* args , char* );

@@syscall:*:execve:entry
{
        __auto String command[128];
        __auto String argument[128];
        __auto struct arg_t argv;
        copy_userdata( __arg2 , argv );
        command = get_userstring( __arg1 , -1 );
        argument = get_userstring( argv.u.arg[0] , -1 );
        printf( "%llu: %s called execve(%s) with arguments: %s " , __pid , __pname , command , argument )
;
        if ( argv.u.num[1] != 0 )
        {
                argument = get_userstring( argv.u.arg[1] , -1 );
                printf( "%s " , argument );
                if ( argv.u.num[2] != 0 )
                {
                        argument = get_userstring( argv.u.arg[2] , -1 );
                        printf( "%s " , argument );
                        if ( argv.u.num[3] != 0 )
                        {
                                argument = get_userstring( argv.u.arg[3] , -1 );
                                printf( "%s " , argument );
                                if ( argv.u.num[4] != 0 )
                                {
                                        argument = get_userstring( argv.u.arg[4] , -1 );
                                        printf( "%s " , argument );
                                }
                        }
                }
        }
        printf( "\n" );
}

@@sysproc:exit:*
when ( __exitinfo->signo == 11 )
{
        printf( "%llu:  %s\n" , __pid , __pname );
        ptree(10);
}

Das Skript haben wir capture_segv.e genannt und ausführbar gemacht.

In der Theorie sollte das Programm nach dem Start alle startenden Programme mit PID, Namen und bis zu 5 Argumenten ausgeben. Außerdem erfolgt eine Ausgabe wenn ein Prozess mit dem Signal 11 (SIGSEGV) abgebrochen wird. Die entsprechende PID kann man dann weiter oben in der Ausgabe suchen und damit das Programm mit Argumenten identifizieren.

Leider ergibt sich in der Praxis das folgende kleine Problem: wenn ein Programm nach dem execve sehr schnell beendet wird, bevor ProbeVue mit get_userstring die Argumente kopieren kann, kommt es bei get_userstring zu einem Zugriff auf eine nicht mehr existierende Adresse und das ProbeVue Skript wird abgebrochen. Wir haben dies umgangen, indem wir das ProbeVue Skript einfach über eine Endlos-Schleife immer wieder starten:

# while true; do ./capture_segv.e >>/tmp/wait_for_segv ; done

Wir haben das ProbeVue Skript dann einige Stunden laufen lassen, bis es dann wieder zu einem Core-File von Perl kam. Die Datei /tmp/wait_for_segv enthielt ca 23.000 Zeilen! Wir haben hier nur die relevanten Zeilen aufgelistet:

# cat /tmp/wait_for_segv
…
8651210: ksh called execve(xxxx_hacheck.pl) with arguments: xxxx_hacheck.pl -c
8651210: ksh called execve(/var/opt/OV/bin/instrumentation/xxxx_hacheck.pl) with arguments: xxxx_hacheck
.pl -c
20054518: ksh called execve(/bin/proc2mon.pl) with arguments: proc2mon.pl
…
8651210:  perl

     PID              CMD
       1              init
                        |
                        V
9634196              ovcd
                        |
                        V
9765232              opcacta
                        |
                        V
8651210              perl    <=======
…

Man sieht das Perl über das Programm opcacta gestartet wurde, welches selbst von ovcd gestartet wurde. Diese Prozesse gehören zu HP OpenView das hier im Einsatz ist. Weiter oben in der Ausgabe kann man sehen das das Perl-Skript /var/opt/OV/bin/instrumentation/xxxx_hacheck.pl gestartet wurde. Damit haben wir das Skript gefunden das die vielen Core-Files erzeugt.

Das Skript wurde erst kürzlich geschrieben und muß offensichtlich noch einmal untersucht und überarbeitet werden.

Mit Hilfe von ProbeVue hat ein kurzes Skript und einige Stunden Warten ausgereicht um die Ursache des Problems zu finden! ProbeVue ist aber nicht nur bei der Untersuchung von Problemen nützlich; auch beim Performance Monitoring erweist sich ProbeVue als extrem hilfreich.

 

Zugriff auf das Ablaufdatum des Update Access Keys von AIX aus

Im Zuge der Einführung von POWER8 Systemen hat IBM auch den „Update Access Key“ eingeführt, der für das Durchführen von Firmware Updates des Managed Systems notwendig ist. Neu ausgelieferte System haben standardmäßig einen Update Access Key der in der Regel nach 3 Jahren abläuft. Danach kann der Update Access Key bei bestehen eines Wartungsvertrages jeweils um ein halbes Jahr verlängert werden (https://www.ibm.com/servers/eserver/ess/index.wss).

Wann der aktuelle Update Access Key abläuft lässt sich natürlich leicht über die HMC herausfinden, GUI oder CLI. Man kann das Ablaufdatum aber auch über das Kommando lscfg von AIX aus anzeigen:

Im Falle von AIX 7.1 sieht dies so aus:

$ lscfg -vpl sysplanar0 | grep -p "System Firmware"
      System Firmware:
...
        Microcode Image.............SV860_138 SV860_103 SV860_138
        Microcode Level.............FW860.42 FW860.30 FW860.42
        Microcode Build Date........20180101 20170628 20180101
        Microcode Entitlement Date..20190825
        Hardware Location Code......U8284.22A.XXXXXXX-Y1
      Physical Location: U8284.22A.XXXXXXX-Y1

Im Falle von AIX 7.2 ist die Ausgabe geringfügig anders:

$ lscfg -vpl sysplanar0 |grep -p "System Firmware"
      System Firmware:
...
        Microcode Image.............SV860_138 SV860_103 SV860_138
        Microcode Level.............FW860.42 FW860.30 FW860.42
        Microcode Build Date........20180101 20170628 20180101
        Update Access Key Exp Date..20190825
        Hardware Location Code......U8284.22A.XXXXXXX-Y1
      Physical Location: U8284.22A.XXXXXXX-Y1

Relevant sind die Zeilen „Microcode Entitlement Date“ bzw. „Update Access Key Exp Date„.

Besonderheiten von NFSv4 Mounts

Viele AIX Administratoren verwenden NFSv4 genauso wie zuvor NFSv3 und NFSv2. Beim Exportieren und Mounten wird die Version 4 angegeben, ansonsten wird alles wie bisher gemacht. Das funktioniert zwar in den meisten Fällen, verhindert aber gleichzeitig die Nutzung einiger interessanter Eigenschaften von NFSv4.

Der erste bedeutende Unterschied zwischen NFSv4 und seinen Vorgängern besteht schon beim Mounten. Bei NFSv2 und NFSv3 wird hierzu ein separates MOUNT-Protokoll verwendet. Soll beispielsweise der folgende NFS-Mount ausgeführt werden:

clientv3 # mount aixnim:/export/data /mnt
clientv3 #

dann wird eine RPC-Anfrage and den rpc.mountd auf dem NFS-Server gesendet, um ein NFS-Filehandle für das Dateisystem/Verzeichnis /export/data zu bekommen. Bei allen NFS-Operationen muß immer ein NFS-Filehandle angegeben werden, welches auf dem NFS-Server eindeutig eine Datei oder Verzeichnis identifiziert. Das allererste Filehandle wird über das MOUNT-Protokoll erfragt.

Im Falle von NFSv4 sieht dies ganz anders aus, es wird kein separates MOUNT-Protokoll benötigt. Anstelle dessen wird ein sogenanntes root-Filehandle verwendet, dieses ist über den NFS-Standard definiert und muß nicht über ein separates Protokoll in Erfahrung gebracht werden. Bei dem folgenden NFSv4-Mount

clientv4 # mount –o vers=4 aixnim:/export/data /mnt
clientv4 #

Startet der Client ein NFS-LOOKUP, wobei er das wohlbekannte (vom NFS-Standard definierte) root-Filehandle angibt, sowie den dazu relativen Pfad „export/data“, dann liefert der NFS-Server das zugehörige Filehandle zurück. Dies läßt sich mit Hilfe von tcpdump leicht verfolgen, was wir aus Platzgründen aber hier nicht getan haben.

Ein für viele Administratoren überraschender (und vielleicht nicht immer verstandener) Nebeneffekt ist, das man mit „showmount –e“ nicht die mit NFSv4 exportierten Filesysteme sehen kann. Das liegt aber einfach daran das es kein MOUNT-Protokoll für NFSv4 gibt. Damit kann man auf dem NFS-Client nicht so ohne weiteres herausfinden welche Filesysteme der NFS-Server für NFSv4 exportiert hat.

clientv4 # showmount -e aixnim
no exported file systems for aixnim
clientv4 #

Das Kommando „showmount –e“ zeigt keine exportierten Filesysteme, obwohl wir oben erfolgreich mittels NFSv4 mounten konnten. Wir kommen später noch einmal darauf zurück.

Der zweite bedeutende Unterschied ist das für NFSv4 der NFS-Server für jeden NFSv4 Client ein Pseudo-Filesystem erzeugt. Dieses Filesystem startet bei dem nfsroot-Verzeichnis (per Default ist das /) und beinhaltet alle darunter liegenden, für den Client exportierten, Verzeichnisse und Dateien. Das Pseudo-Filesystem wird auch dann erzeugt, wenn es für den Client kein exportiertes Filesystem gibt das er mounten könnte!

Zur Demonstration haben wir auf unserem NFS-Server aixnim keine exportieren Filesysteme zur Verfügung gestellt:

aixnim # lsnfsexp
aixnim #

Obwohl für den NFS-Client noch nichts exportiert wurde, kann das für den Client generierte Pseudo-Filesystem trotzdem mittels NFSv4 gemountet werden:

clientv4 # mount -o vers=4 aixnim:/ /mnt
clientv4 # ls -il /mnt
total 0
clientv4 #

Das gemountete Filesystem ist natürlich leer, da ja noch nichts exportiert wurde. Wir hängen das gemountete Filesystem wieder aus (hier nicht gezeigt) und exportieren auf dem NFS-Server aixnim das Verzeichnis /var/adm:

aixnim # mknfsexp -d /var/adm -v 4 -r clientv4
aixnim # lsnfsexp
/var/adm -vers=4,root=clientv4
aixnim #

Wir mounten nun erneut das Pseudo-Filesystem ab /:

clientv4 # mount -o vers=4 aixnim:/ /mnt
clientv4 #

Um die Unterschiede zu NFSv2 und NFSv3 leichter illustrieren zu können, zeigen wir kurz noch das nützliche Kommando nfs4cl für den NFSv4-Client:

clientv4 # nfs4cl showfs

Server      Remote Path          fsid                 Local Path        
--------    ---------------      ---------------      ---------------   
aixnim    /                    0:42949672964        /mnt              
clientv4 #

Das Kommando zeigt das von aixnim gemountete Pseudo-Filesystem /, das unter /mnt gemountet ist. Wir schauen nun kurz mit dem Kommando ls in das Verzeichnis /mnt

clientv4 # ls -il /mnt
total 1
    2 dr-xr-xr-x    2 root     system            3 May 21 07:34 var
clientv4 #

In dem vom NFS-Server generierten Pseudo-Filesystem ist nur die Pfad-Komponente /var sichtbar. Diese Pfad-Komponente ist ein Prefix des exportierten Verzeichnisses /var/adm. Andere Verzeichnisse wie /opt oder /usr sind im Pseudo-Filesystem nicht sichtbar, da diese nicht Prefix eines exportierten Pfades sind. Wir werfen einen Blick auf /mnt/var:

clientv4 # ls -il /mnt/var   
total 8
   32 drwxrwxr-x   15 root     adm            4096 May  2 11:30 adm
clientv4 #

Auch unter var ist nur das Verzeichnis adm sichtbar, da nur /var/adm ein Prefix eines exportierten Pfades ist. Das Pseudo-Filesystem ist natürlich an den Stellen die nicht exportiert wurden unveränderbar, wie der Versuch eine Datei unter /mnt/var anzulegen zeigt:

clientv4 # touch /mnt/var/file
touch: /mnt/var/file cannot create
clientv4 #

Ab /mnt/var/adm sieht dann alles wie von NFSv2 und NFSv3 gewohnt aus, man hat Zugriff auf die exportierten Daten:

clientv4 # ls -il /mnt/var/adm
total 704
  110 drw-r-----    2 root     system          256 May 20 14:33 SRC
4165 drwxrwxr-x    2 adm      adm             256 Apr 17 08:07 acct
   70 drwx------    4 root     system          256 Apr 17 07:50 config
4133 drwx------    2 root     system          256 Apr 17 08:03 corrals
...
4   33 -rw-rw-r--    1 adm      adm          337608 May 20 09:30 wtmp
clientv4 #

Schauen wir uns jetzt noch einmal die Ausgabe des Kommandos “nfs4cl showfs” an:

Clientv4 # nfs4cl showfs

Server      Remote Path          fsid                 Local Path        
--------    ---------------      ---------------      ---------------   
aixnim   /var                 0:42949672966        /mnt/var          
aixnim   /                    0:42949672964        /mnt        
clientv4 #

Für jedes physikalische Filesystem auf dem Server wird ein eigenes Pseudo-Filesystem erzeugt. Das jeweilige Pseudo-Filesystem gewährt Zugriff auf exportierte Verzeichnisse des unterliegenden physikalischen Filesystems und generiert für Pfad-Prefixe von exportierten Verzeichnissen read-only Verzeichnisse.

Wir exportieren auf dem NFS-Server aixnim noch das Verzeichnis /usr/share für den Client:

aixnim # mknfsexp -d /usr/share -v 4 -r clientv4
aixnim # lsnfsexp
/var/adm -vers=4,root=clientv4
/usr/share -vers=4,root=clientv4
aixnim #

Auf dem Client führen wir dieses Mal keinen umount und erneuten Mount aus, sondern greifen mittels ls einfach noch einmal auf den Mountpunkt /mnt zu:

clientv4 # ls -il /mnt
total 2
    2 dr-xr-xr-x    2 root     system            3 May 21 08:13 usr
    2 dr-xr-xr-x    2 root     system            3 May 21 07:34 var
clientv4 #

Der Pfad-Prefix usr des gerade exportierte Verzeichnisses /usr/share taucht auf dem Client auf, ohne das wir explizit gemountet haben. Ein Blick nach /mnt/usr zeigt das wie erwartet das Verzeichnis share auftaucht:

clientv4 # ls -il /mnt/usr

total 0

16390 drwxr-xr-x    8 bin      bin             256 Apr 17 08:31 share

clientv4 #

Und unter /mnt/usr/share befinden sich dann wie erwartet die exportierten Daten:

clientv4 # ls -il /mnt/usr/share
total 24
74212 drwxr-xr-x    2 bin      bin             256 Apr 17 08:24 X11
49162 drwxr-xr-x    2 bin      bin             256 Nov  3 2015  dict
16391 drwxr-xr-x   12 bin      bin            4096 Apr 17 08:22 lib
17762 lrwxrwxrwx    1 root     system           26 Apr 17 08:31 locale -> /opt/freeware/share/locale
20653 drwxr-xr-x    5 root     system          256 Apr 24 15:46 lpp
16911 drwxr-xr-x   11 bin      bin            4096 May 20 14:25 man
45096 drwxr-xr-x    2 bin      bin            4096 Apr 17 08:03 modems
clientv4 #

Das Kommando „nfs4cl showfs“ zeigt nun 3 Filesysteme:

Clientv4 # nfs4cl showfs

Server      Remote Path          fsid                 Local Path        
--------    ---------------      ---------------      ---------------   
aixnim /usr                 0:42949672965        /mnt/usr          
aixnim /var                 0:42949672966        /mnt/var          
aixnim /                    0:42949672964        /mnt              
clientv4 #

Das letzte Beispiel zeigt das im Falle von NFSv4 neue Filesysteme nicht zwangsläufig manuell auf dem Client gemountet werden müssen. Werden weitere Filesysteme auf dem NFS-Server für einen NFSv4-Client exportiert, dann kann der Client einfach auf das neue Filesystem zugreifen. Voraussetzung ist allerdings das der Pfad für das neue Filesystem Bestandteil des vom Client gemounteten Pseudo-Filesystems ist. Da wir das komplette Pseudo-Filesystem ab dem nfsroot gemountet hatten, ist dies trivialerweise der Fall. Hätten wir aber lediglich /var vom NFS-Server gemountet, dann wäre /usr/share nicht Bestandteil des Pseudo-Filesystems von /var und es wäre ein separater Mount erforderlich gewesen.

Das weitere Filesysteme wie gerade gezeigt ohne explizites Mounten verfügbar sind, liegt in einem dritten Unterschied von NFSv4 zu seinen Vorgängern. Bei NFSv2 und NFSv3 sind alle Filehandle persistent, das heißt unveränderlich. Mit NFSv4 wurden volatile (zerbrechliche) Filehandle zusätzlich zu den persistent Filehandles eingeführt. Die vom Pseudo-Filesystem verwendeten Filehandle sind volatile. Das heißt der Client muß damit rechnen das sich ein solches Filehandle ändern kann. Dies ist der Fall wenn ein weiterer Pfad auf dem NFS-Server exportiert wurde, die Filehandles für die Pfad-Prefixe im Pseudo-Filesystem ändern sich dann, der Client bekommt dies nach kurzer Zeit mit und reagiert entsprechend.

Abschließend sei noch auf ein kleines Problem hingewiesen: wir hatten einen Mount durchgeführt, das Kommando „nfs4cl showfs“ hat allerdings gezeigt das hierbei zuletzt 3 Filesysteme beteiligt waren. Es ist aber nur ein Filesystem gemountet, wie df zeigt:

clientv4 # df -g
Filesystem    GB blocks      Free %Used    Iused %Iused Mounted on
/dev/hd4           1.00      0.86   15%     8367     5% /
/dev/hd2           6.12      2.90   53%    65563     9% /usr
/dev/hd9var        2.00      1.81   10%     1770     1% /var
/dev/hd3           2.00      1.89    6%      452     1% /tmp
/dev/hd1           1.00      0.88   12%      454     1% /home
/dev/hd11admin      0.50      0.50    1%       15     1% /admin
/proc                 -         -    -        -      - /proc
/dev/hd10opt       2.00      0.94   54%    22460    10% /opt
/dev/livedump      1.00      1.00    1%        4     1% /var/adm/ras/livedump
/dev/varadmloglv      2.00      1.85    8%      275     1% /var/adm/log
aixnim:/       0.38      0.20   47%    12644    22% /mnt
clientv4 #

Das unter /mnt gemountete Filesystem ist 0.38 GB groß. Auf dem NFS-Server wurden /usr/share und /var/adm exportiert, ein df zeigt hier die folgenden Größen:

aixnim # df –g / /usr/share /var/adm
Filesystem    GB blocks      Free %Used    Iused %Iused Mounted on
/dev/hd4           0.38      0.20   47%    12644    22% /
/dev/hd2           2.06      0.23   89%    39236    41% /usr
/dev/hd9var        0.50      0.45   10%      614     1% /var
aixnim #

Offensichtlich werden beim Client die Werte des Filesystems / des NFS-Servers verwendet! Unter /usr und damit auch /usr/share wären noch knapp 2 GB verfügbarer Platz, was allerdings beim Kommando df auf dem Client nicht angezeigt wird. Es dürfte auch schwierig sein auf dem Client Werte anzugeben, da auf dem NFS-Server mehrere Filesysteme involviert sind. Das Kommando df zeigt hier einfach die Daten des dem gemounteten Pseudo-Filesystem unterliegenden physikalischen Filesystems an. In unserem Falle ist dies das root-Filesystem des NFS-Servers. Helfen kann hier wieder das Kommando nfs4cl, dieses besitzt ein Subkommando zum Anzeigen von Filesystem-Informationen ähnlich zu df:

clientv4 # nfs4cl showstat

Filesystem       512-blocks        Free  %Used       Iused %Iused  Mounted on
aixnim:/usr     4325376      482752    89%       39236    41% /mnt/usr  
aixnim:/var     1048576      947064    10%         614     1% /mnt/var  
aixnim:/      786432      417944    47%       12644    22% /mnt     
clientv4 #

Dies ist identisch zu den Werten die bei df auf dem NFS-Server angezeigt werden.

Aber auch mit dem Standard-df von AIX kann man diese Information bekommen, wie die folgende Ausgabe zeigt:

clientv4 # df -g /mnt /mnt/usr /mnt/usr/share /mnt/var /mnt/var/adm
Filesystem    GB blocks      Free %Used    Iused %Iused Mounted on
aixnim:/           0.38      0.20   47%    12644    22% /mnt
[NFSv4]            2.06      0.23   89%    39236    41% /mnt/usr
[NFSv4]            2.06      0.23   89%    39236    41% /mnt/usr/share
[NFSv4]            0.50      0.45   10%      614     1% /mnt/var
[NFSv4]            0.50      0.45   10%      614     1% /mnt/var/adm
clientv4 #

Es gibt natürlich noch eine Reihe weiterer Unterschiede, die aber hier nicht mehr angeschaut werden sollen. Vielleicht wird es noch einen weiteren Artikel zu dem Thema geben.

Nützlich sind die oben dargestellten Vorteile insbesondere bei hierarchischen Mounts. Bei NFSv4 muß man nur einen Mount durchführen und sich damit nicht mehr um die Reihenfolge der Mounts kümmern.

Das Verstehen der Funktionsweise des Pseudo-Filesystems für den NFSv4-Client hilft beim Ermitteln der exportierten Filesysteme auf dem Client. Anstelle „showmount -e“ wie bisher bei NFSv2 und NFSv3 zu benutzen (was bei NFSv4 kein Resultat erzielt), kann man einfach alles ab / mounten und dann mit cd und ls herausfinden was der NFS-Server exportiert hat.

 

Extrem schnell wachsendes /var/adm/wtmp

Kürzlich hatten wir auf einem unserer AIX SAP-Systeme ein volles /var-Filesystem. Es stellte sich heraus das ein auf 1.9 GB angewachsenes /var/adm/wtmp-File die Ursache war. Diese Datei war innerhalb kurzer Zeit auf fast 2 GB angewachsen. Es stellte sich die Frage was die extrem vielen Einträge produzierte. Um dies festzustellen wurde erst einmal der Inhalt der Datei in ASCII-Form angezeigt:

# cat /var/adm/wtmp  | /usr/sbin/acct/fwtmp
         ac02                         8 25690134 0000 0177 1558338990                                  Mon May 20 09:56:30 DFT 2019
         ac01                         8 27525310 0000 0177 1558338990                                  Mon May 20 09:56:30 DFT 2019
         ac00                         8 27525308 0000 0177 1558338990                                  Mon May 20 09:56:30 DFT 2019
ac00     ac00                         5 7864366 0000 0000 1558338990                                  Mon May 20 09:56:30 DFT 2019
ac01     ac01                         5 7864368 0000 0000 1558338990                                  Mon May 20 09:56:30 DFT 2019
ac02     ac02                         5 7864370 0000 0000 1558338990                                  Mon May 20 09:56:30 DFT 2019
         ac01                         8 7864368 0000 0177 1558338990                                  Mon May 20 09:56:30 DFT 2019
         ac00                         8 7864366 0000 0177 1558338990                                  Mon May 20 09:56:30 DFT 2019
…
#

Diese Einträge wiederholten sich endlos, teilweise gab es mehr als 50 Einträge innerhalb einer Sekunde! Die Zeichenketten „ac00“, „ac01“ und „ac02“ sind IDs aus der /etc/inittab. In der Spalte 2 bzw. 3 steht der Typ des Eintrags, hier 5 und 8. Die Bedeutung lässt sich über die Header-Datei /usr/include/utmp.h herausfinden:

# cat /usr/include/utmp.h
…
/*      Definitions for ut_type                                         */
…
#define INIT_PROCESS    5       /* Process spawned by "init" */
…
#define DEAD_PROCESS    8
…

Die Prozesse wurden von /etc/init gestartet und sind dann aber sofort wieder verstorben. Es sieht so aus, als würden hier Prozesse mit der Aktion „respawn“ gestartet, welche dann aufgrund eines Fehlers sofort wieder beendet werden. Wir schauen uns die zugeörigen inittab-Einträge an:

#  lsitab ac00    
ac00:2345:respawn:/oracle/NW1/acs/acsgen -D
#  lsitab ac01
ac01:2345:respawn:/oracle/NW1/acs/acsd
#  lsitab ac02
ac02:2345:respawn:/oracle/NW1/acs/fcmcli -D
#

Es handelt sich hier um Einträge von Oracle, die offensichtlich nicht wie beabsichtigt funktionieren.

In unserem Falle existierten schlicht die Binaries nicht an der angegebenen Stelle:

#  ls -l /oracle/NW1/acs/acsgen /oracle/NW1/acs/acsd /oracle/NW1/acs/fcmcli
ls: 0653-341 The file /oracle/NW1/acs/acsgen does not exist.
ls: 0653-341 The file /oracle/NW1/acs/acsd does not exist.
ls: 0653-341 The file /oracle/NW1/acs/fcmcli does not exist.
#

In Absprache mit den Oracle-Kollegen wurden die Einträge aus der /etc/inittab entfernt und damit das Problem behoben:

# rmitab ac00
# rmitab ac01
# rmitab ac02
#

Fehlerhafte Einträge in der /etc/inittab können eine schnell wachsende /var/adm/wtmp zur Folge haben.

 

AIX: Anwendungen des namefs-Filesystems

Gelegentlich benötigt man ein Verzeichnis (oder ein Filesystem) an einer anderen Stelle im Filesystem oder vielleicht sogar an mehreren verschiedenen Stellen im Filesystem. Anstatt das Problem mit symbolischen Links zu lösen, kann man elegant das namefs-Filesystem zum Einsatz bringen.

In folgenden Beispie wird /data/in an anderer Stelle benötigt:

# ls -l /data/in
total 16
-rw-r--r--    1 root     system          554 May 14 16:10 file1
-rw-r--r--    1 root     system          381 May 14 16:10 file2
# ls -l /other/place
total 0
#

Mounten des Directories an die gewünschte Stelle /other/place:

# mount -v namefs /data/in /other/place
# ls -l /other/place
total 16
-rw-r--r--    1 root     system          554 May 14 16:10 file1
-rw-r--r--    1 root     system          381 May 14 16:10 file2
#

Der Mount mit dem namefs-Filesystem bietet zusätzlich die Möglichkeit Mount-Optionen anzugeben, die dann nur für das Verzeichnis gelten. Man kann damit z.B. ein Verzeichnis auch mit Direct-I/O Mounten, obwohl das ursprüngliche Verzeichnis nicht mit Direct-I/O gemountet wurde:

# mount -v namefs -o dio /data/in /other/place
# mount
  node       mounted        mounted over    vfs       date        options     
-------- ---------------  ---------------  ------ ------------ ---------------
         /dev/hd4         /                jfs2   May 02 11:30 rw,log=/dev/hd8
...
         /data/in         /other/place     namefs May 14 16:14 rw,dio         
#

Bei Zugriffen auf die Dateien unterhalb /other/place wird nun Direct-I/O verwendet, greift man aber über die „Originale“ unter /data/in zu, wird kein Direct-I/O verwendet!

Der Zugriff auf Dateien ist allerdings wie bei NFS auf das unterliegende physikalische Filesystem beschränkt. Dies läßt sich leicht anhand des Filesystems / demonstrieren. Wir mounten / per namefs unter /mnt und schauen uns /mnt/usr und /mnt/var an:

# mount -v namefs / /mnt
# ls -l /mnt/usr /mnt/var
/mnt/usr:
total 0
lrwxrwxrwx    1 root     system           11 Apr 17 07:49 lib -> /../usr/lib

/mnt/var:
total 0
#

Die Verzeichnisse sind leer bzw. enthalten einen symbolischen Link, /usr und /var sehen etwas anders aus!

Dies kann man natürlich auch ausnutzen, z.B. in Fällen bei denen interessante Daten übermountet wurden. Wir haben unterhalb von /home eine Datei abgelegt, bevor /dev/hd1 auf /home gemountet wurde. Über das gerade auf /mnt gemountet root-Filesystem kann man auf diese übermounteten Daten zugreifen:

# ls -l /mnt/home
total 0
-rw-r--r--    1 root     system            0 May 14 17:48 overmounted_file
#

Eine weitere Anwendung besteht darin ein Verzeichnis vor überschreiben zu schützen. Wir demonstrieren dies am Verzeichnis /data mit 2 Test-Dateien:

# ls -l /data
total 16
-rw-r--r--    1 root     system          554 May 14 17:52 file1
-rw-r--r--    1 root     system          381 May 14 17:52 file2
# cp /etc/hosts /data
# ls -l /data
total 24
-rw-r--r--    1 root     system          554 May 14 17:52 file1
-rw-r--r--    1 root     system          381 May 14 17:52 file2
-rw-r--r--    1 root     system         2075 May 14 17:54 hosts
#

Überschreiben oder Ändern von Daten ist aktuell noch möglich, wie das erfolgreiche cp-Kommando zeigt. Jetzt schützen wir die Daten, indem wir einen Mount mit dem namefs-Filesystem und der Option ro (Read-Only) durchführen:

# mount -v namefs -o ro /data /data
# cp /etc/services /data
cp: /data/services: Read-only file system
#

Die Daten können offensichtlich nicht mehr geändert werden. Hier haben wir /data mit einer Read-only Version von sich selbst übermountet!

Mounts mit dem Pseudo-Filesystem namefs können nicht nur auf jfs2-Filesystemen, sondern auch für NFS-Filesysteme oder das procfs-Filesystem durchgeführt werden.

Als Letztes zeigen wir noch das Mounten einer Datei an einer anderen Stelle des Filesystems. Wir wollen die Datei /etc/hosts über den Namen /hosts verfügbar machen. Dazu legen wir zunächst eine leere Datei /hosts an und mounten dann die Datei /etc/hosts über diese leere Datei:

# touch /hosts
# ls -l /hosts
-rw-r--r--    1 root     system            0 May 14 17:59 /hosts
# mount -v namefs /etc/hosts /hosts
# ls -l /hosts
-rw-rw-r--    1 root     system         2075 Apr 26 10:47 /hosts
#

Vor dem Mount ist /hosts 0 Bytes groß, nach dem Mount 2075 Bytes!

Das namefs-Filesystem bietet also einige interessante Möglichkeiten, die bei einige Problemstellungen nützlich sein können.

 

 

 

Migration nach AIX PCM kombiniert mit OS-Update

Bei den meisten AIX Systemen wird wahrscheinlich in regelmäßigen Abständen der SP- oder TL-Level aktualisiert. Es bietet sich an, bei einem solchen Update, die Migration von SDDPCM nach AIX PCM mit durchzuführen. Dies spart Zeit und einige Reboots, die ansonsten extra wegen der Multipathing Migration durchgeführt werden müssen.

In unserem Blog-Beitrag „Migration von SDDPCM nach AIX-PCM“ hatten wir die Migration für Standalone Systeme schon gezeigt.

Hier soll die Migration von SDDPCM nach AIX-PCM im Rahmen eines OS-Updates gezeigt werden, wobei das Alternate Disk Copy Verfahren zum Einsatz kommt. Der Ablauf ist im groben der folgende:

  1. Entspiegeln der rootvg um eine freie Platte für Alternate Disk Copy zu gewinnen.
  2. Ändern des Path Control Moduls (PCM) auf AIX PCM.
  3. Erzeugen der altinst_rootvg.
  4. Entfernen von Fixes in der altinst_rootvg.
  5. Durchführen des OS-Updates auf der altinst_rootvg.
  6. Installation von Fixes in der altinst_rootvg.
  7. Hinzufügen eines firstboot-Skripts um Platten-Attribute zu setzen.
  8. Ändern des Path Control Moduls (PCM) zurück auf SDDPCM.
  9. Booten von der altinst_rootvg.

Auf unserem Beispiel-System ist AIX 7.1 TL5 SP2 installiert, die Platten sind SVC-Platten, die über virtual FC Adapter angebunden sind. SDDPCM ist der aktuell aktive Multipathing-Treiber:

# oslevel -s
7100-05-02-1810
# lsdev -l hdisk0 -F uniquetype
disk/fcp/2145
aix01:/root> lsattr -El hdisk0 -a PCM -F value
PCM/friend/sddpcm
#

Wie schon im oben genannten Blog-Beitrag ausgeführt, ändern sich einige Platten-Attribute bei der Migration auf AIX PCM. Daher sollte man sich die aktuellen Attribute genau anschauen, um diese gegebenenfalls später zu übernehmen (zumindest teilweise). Exemplarisch schauen wir uns hier nur das Attribut queue_depth an, welches aktuell den Wert 120 hat:

# lsattr -El hdisk0 -a queue_depth -F value
120
#

Unser System besitzt eine gespiegelte rootvg:

# lsvg -p rootvg
rootvg:
PV_NAME           PV STATE          TOTAL PPs   FREE PPs    FREE DISTRIBUTION
hdisk3            active            399         232         00..01..71..80..80
hdisk0            active            399         240         00..01..79..80..80
#

Das System wurde von der hdisk0 gebootet:

# bootinfo -b
hdisk0
#

Daher lassen wir die hdisk0 in der rootvg und entfernen die hdisk3 aus der rootvg, um eine freie Platte für Alternate Disk Copy zu gewinnen.

# unmirrorvg rootvg hdisk3
0516-1246 rmlvcopy: If hd5 is the boot logical volume, please run 'chpv -c <diskname>'
        as root user to clear the boot record and avoid a potential boot
        off an old boot image that may reside on the disk from which this
        logical volume is moved/removed.
0516-1804 chvg: The quorum change takes effect immediately.
0516-1144 unmirrorvg: rootvg successfully unmirrored, user should perform
        bosboot of system to reinitialize boot records.  Then, user must modify
        bootlist to just include:  hdisk0.
# reducevg rootvg hdisk3
# chpv -c hdisk3
# bootlist -m normal hdisk0
#

Bevor wir nun mittels Alternate Disk Copy eine Kopie der rootvg erzeugen, stellen wir das System temporär auf AIX PCM um, ohne allerdings zu rebooten. Wird dann anschließend die altinst_rootvg erzeugt, dann ist in dieser die Umstellung auf AIX PCM schon vorgenommen!

# manage_disk_drivers -d IBMSVC -o AIX_AAPCM
********************** ATTENTION *************************
  For the change to take effect the system must be rebooted
#

Am Ende des OS-Updates machen wir diese Umstellung dann auf der rootvg wieder rückgängig, um den Original-Zustand mit SDDPCM zu haben.

Nach diesen Vorbereitungen starten wir nun das alt_disk_copy Kommando:

# alt_disk_copy -d hdisk3 -B
Calling mkszfile to create new /image.data file.
Checking disk sizes.
Creating cloned rootvg volume group and associated logical volumes.
Creating logical volume alt_hd5.
Creating logical volume alt_hd6.
Creating logical volume alt_hd8.
…
#

Auf dem System sind einige EFixes installiert, die wir vor dem Update noch aus der altinst_rootvg entfernen:

# emgr -l
ID  STATE LABEL      INSTALL TIME      UPDATED BY ABSTRACT
=== ===== ========== ================= ========== ======================================
1    S    102m_ifix  10/14/18 10:48:18            IFIX for Openssl CVE on 1.0.2m       
2    S    IJ03121s0a 10/14/18 10:49:04            IJ03121 for AIX 7.1 TL5 SP00         
3    S    IJ05822s2a 10/14/18 10:49:18            a potential security issue exists    
…
#

Aktivieren der altinst_rootvg:

# alt_rootvg_op -W -d hdisk3
Waking up altinst_rootvg volume group ...
#

Und entfernen der EFixes:

# INUCLIENTS=1 /usr/sbin/chroot /alt_inst /usr/sbin/emgr –r -n 3
+-----------------------------------------------------------------------------+
Efix Manager Initialization
+-----------------------------------------------------------------------------+
Initializing log /var/adm/ras/emgr.log ...
Accessing efix metadata ...
Processing efix label "IJ05822s2a" ...
…
Operation Summary
+-----------------------------------------------------------------------------+
Log file is /var/adm/ras/emgr.log

EFIX NUMBER       LABEL               OPERATION              RESULT           
===========       ==============      =================      ==============   
1                 IJ05822s2a          REMOVE                 SUCCESS          

Return Status = SUCCESS
# INUCLIENTS=1 chroot /alt_inst /usr/sbin/emgr -r -n 2
…
# INUCLIENTS=1 chroot /alt_inst /usr/sbin/emgr -r -n 1
…

(Hinweis: Bitte nicht die Variable INUCLIENTS vergessen, diese signalisiert das die Operation in einem Alternate Boot Environment stattfindet!)

Wir mounten nun die LPP-Source für den OS-Update per NFS von unserem NIM-Server:

# mount aixnim:/export/nim/lpps/aix710503lpp /mnt
#

Nun kann der OS-Update in der altinst_rootvg durchgeführt werden:

# alt_rootvg_op -C -b update_all -l /mnt
Installing optional filesets or updates into altinst_rootvg...
install_all_updates: Initializing system parameters.
install_all_updates: Log file is /var/adm/ras/install_all_updates.log
install_all_updates: Checking for updated install utilities on media.
…
installp:  * * * A T T E N T I O N ! ! !
        Software changes processed during this session require
        any diskless/dataless clients to which this SPOT is
        currently allocated to be rebooted.
install_all_updates: Log file is /var/adm/ras/install_all_updates.log
install_all_updates: Result = SUCCESS
#

Abschließend installieren wir noch einige EFixes, dazu mounten wir zunächst das Verzeichnis /mnt mit den Fixes in die altinst_rootvg:

# mount -v namefs /mnt /alt_inst/mnt
# INUCLIENTS=1 chroot /alt_inst /usr/sbin/emgr -e /mnt/emgr/ppc/102p_fix.181127.epkg.Z
+-----------------------------------------------------------------------------+
Efix Manager Initialization
+-----------------------------------------------------------------------------+
Initializing log /var/adm/ras/emgr.log ...
Efix package file is: /mnt/emgr/ppc/102p_fix.181127.epkg.Z
…
EPKG NUMBER       LABEL               OPERATION              RESULT           
===========       ==============      =================      ==============   
1                 102p_fix            INSTALL                SUCCESS          

Return Status = SUCCESS
# INUCLIENTS=1 chroot /alt_inst /usr/sbin/emgr -e /mnt/emgr/ppc/IJ09621s3a.181001.epkg.Z
…
# INUCLIENTS=1 chroot /alt_inst /usr/sbin/emgr -e /mnt/emgr/ppc/IJ11545s0a.181127.epkg.Z
…
# umount /alt_inst/mnt
#

Um die gewünschten Platten-Attribute zu setzen und um SDDPCM zu deinstallieren verwenden wir ein firstboot-Skript:

# cat /alt_inst/etc/firstboot
#! /bin/ksh

print "INFO: adjust hdisk attributes"
chdev -Pl hdisk0 -a queue_depth=120

print "INFO: uninstalling SDDPCM"
installp -u devices.sddpcm.$(uname -v)$(uname -r).rte devices.fcp.disk.ibm.mpio.rte

print "INFO: perform reboot"
reboot

# chmod a+x /alt_inst/etc/firstboot
#

Das Skript sollte, falls man es verwendet, auf die eigenen Bedürfnisse angepaßt werden und man sollte dort alle gewünschten Platten-Attribute anpassen (queue_depth, reserve_policy,…). Das Beispiel-Skript hier soll nur andeuten was man alles machen könnte!

Die altinst_rootvg ist nun aktualisiert und auf AIX PCM umgestellt. Wir deaktivieren die altinst_rootvg, damit von dieser gebootet werden kann.

# alt_rootvg_op –S -t
Putting volume group altinst_rootvg to sleep ...
forced unmount of /alt_inst/var/adm/ras/livedump
…
forced unmount of /alt_inst
Fixing LV control blocks...
Fixing file system superblocks...
#

(Hinweis: Bitte nicht die Option “-t” vergessen, diese erzeugt ein neues Boot-Image!)

Bevor wir jedoch nun von der altinst_rootvg booten, stellen wir auf der rootvg den Multipathing Treiber wieder zurück auf SDDPCM!

# manage_disk_drivers -d IBMSVC -o NO_OVERRIDE
********************** ATTENTION *************************
  For the change to take effect the system must be rebooted
#

Abschließend stellen wir noch die Bootliste auf die altinst_rootvg (hdisk3) um:

# bootlist -m normal hdisk3
#

Und als Letztes rebooten wir nun:

# shutdown –r now

SHUTDOWN PROGRAM
Tue Apr 16 19:49:08 CEST 2019

Broadcast message from root@aix01 (tty) at 19:49:08 ...

PLEASE LOG OFF NOW ! ! !
System maintenance in progress.
All processes will be killed now.
…

-------------------------------------------------------------------------------
                                Welcome to AIX.
                   boot image timestamp: 19:45:08 04/16/2019
                 The current time and date: 19:51:11 04/16/2019
        processor count: 2;  memory size: 4096MB;  kernel size: 36847630
       boot device: /vdevice/vfc-client@3000000a/disk@5005076XXXXXXXXX:2
-------------------------------------------------------------------------------
…
Multi-user initialization completed
INFO: adjust hdisk attributes
hdisk0 changed
INFO: uninstalling SDDPCM
…
Installation Summary
--------------------
Name                        Level           Part        Event       Result
-------------------------------------------------------------------------------
devices.sddpcm.71.rte       2.7.1.1         ROOT        DEINSTALL   SUCCESS   
devices.sddpcm.71.rte       2.7.1.1         USR         DEINSTALL   SUCCESS   
devices.fcp.disk.ibm.mpio.r 1.0.0.25        USR         DEINSTALL   SUCCESS   
INFO: perform reboot
Rebooting . . .
…

AIX Version 7
Copyright IBM Corporation, 1982, 2018.
Console login:

(In der Ausgabe kann man die Aktionen des firstboot-Skriptes sehen: Änderung Plattenattribute, Deinstallation SDDPCM und Reboot.)

Nach dem Einloggen überprüfen wir die OS-Version, den verwendeten Multipathing Treiber und ein paar Platten-Attributes:

# oslevel -s
7100-05-03-1846
# lsdev -l hdisk0 -F uniquetype
disk/fcp/mpioosdisk
# lsattr -El hdisk0 -a PCM -F value
PCM/friend/fcpother
# lsattr -El hdisk0 -a queue_depth -F value
120
# genkex|grep pcm
         5ae0000    60000 /usr/lib/drivers/aixdiskpcmke
# lslpp -l|grep sddpcm
#

Damit haben wir die Migration von SDDPCM nach AIX PCM zusammen mit einem OS-Update erfolgreich durchgeführt. Durch Verskripten lässt sich dies noch automatisieren.

Wir haben dies für AIX 7.1 und AIX 7.2 getestet. Ein Test für PowerHA konnten wir bisher aus zeitlichen Gründen noch nicht durchführen.

Interim Fixes und Alternate Disk Copy

Bei Verwendung des Alternate Disk Copy Verfahrens für AIX Updates kommt es gelegentlich vor das installierte Fixes einen erfolgreichen Update verhindern. Hier bietet es sich an, die installierten Fixes direkt in der altinst_rootvg zu deinstalieren. Dazu kann das emgr Kommando mittels chroot direkt in der altinst_rootvg aufgerufen werden.

Nach dem Erstellen der altinst_rootvg, z.B. mit dem Kommando alt_disk_copy, muß die altinst_rootvg zunächst ersteinmal aktiviert werden:

# alt_rootvg_op -W -d hdisk3
Waking up altinst_rootvg volume group ...
#

Die installierten Fixes kann man sich dann wie folgt auflisten lassen:

# /usr/sbin/chroot /alt_inst /usr/sbin/emgr –l

ID  STATE LABEL      INSTALL TIME      UPDATED BY ABSTRACT
=== ===== ========== ================= ========== ======================================
1    S    102m_ifix  10/14/18 10:48:18            IFIX for Openssl CVE on 1.0.2m       
2    S    IJ03121s0a 10/14/18 10:49:04            IJ03121 for AIX 7.1 TL5 SP00         
3    S    IJ05822s2a 10/14/18 10:49:18            a potential security issue exists    
…

Beim Entfernen von Fixes in der altinst_rootvg ist die Umgebungsvariable INUCLIENTS wichtig. Diese signalisiert dem emgr Kommando keine Dienste zu restarten und keine Geräte dynamisch zu ändern. Ohne das Setzen dieser Variablen schlägt die Deinstallation einiger Fixes in der altinst_rootvg fehl!

# INUCLIENTS=1 /usr/sbin/chroot /alt_inst /usr/sbin/emgr –r -n 3
+-----------------------------------------------------------------------------+
Efix Manager Initialization
+-----------------------------------------------------------------------------+
Initializing log /var/adm/ras/emgr.log ...
Accessing efix metadata ...
Processing efix label "IJ05822s2a" ...
...
Operation Summary
+-----------------------------------------------------------------------------+
Log file is /var/adm/ras/emgr.log

EFIX NUMBER       LABEL               OPERATION              RESULT           
===========       ==============      =================      ==============   
1                 IJ05822s2a          REMOVE                 SUCCESS          

Return Status = SUCCESS
# INUCLIENTS=1 chroot /alt_inst /usr/sbin/emgr -r -n 2
…
# INUCLIENTS=1 chroot /alt_inst /usr/sbin/emgr -r -n 1
…

Nun stehen einem OS-Update keine Fixes mehr im Weg!

Nach dem OS-Update können auf ähnliche Weise neue Fixes noch vor dem Reboot in der altinst_rootvg installiert werden. Wir mounten zunächst das Verzeichnis mit den Fixes unter /alt_inst/mnt:

# mount aixnim:/export/nim/lpps/aix710503lpp /alt_inst/mnt
#

Und installieren anschließend die Fixes wieder mit Hilfe von chroot und INUCLIENTS direkt in der altinst_rootvg:

# INUCLIENTS=1 chroot /alt_inst /usr/sbin/emgr -e /mnt/emgr/ppc/102p_fix.181127.epkg.Z
+-----------------------------------------------------------------------------+
Efix Manager Initialization
+-----------------------------------------------------------------------------+
Initializing log /var/adm/ras/emgr.log ...
Efix package file is: /mnt/emgr/ppc/102p_fix.181127.epkg.Z
…
EPKG NUMBER       LABEL               OPERATION              RESULT           
===========       ==============      =================      ==============   
1                 102p_fix            INSTALL                SUCCESS          

Return Status = SUCCESS
# INUCLIENTS=1 chroot /alt_inst /usr/sbin/emgr -e /mnt/emgr/ppc/IJ09621s3a.181001.epkg.Z
…
# INUCLIENTS=1 chroot /alt_inst /usr/sbin/emgr -e /mnt/emgr/ppc/IJ11545s0a.181127.epkg.Z
…

Als letztes muß die altinst_rootvg dann noch deaktiviert werden, hierbei sollte man auf jeden Fall noch einmal ein neues Boot-Image in der altinst_rootvg erzeugen, ansonsten bleibt das System beim Booten der altinst_rootvg hängen!

# alt_rootvg_op –S -t
Putting volume group altinst_rootvg to sleep ...
forced unmount of /alt_inst/var/adm/ras/livedump
…
forced unmount of /alt_inst
Fixing LV control blocks...
Fixing file system superblocks...
#

(Hinweis: Die Option ‚-t‚ erzwingt das Erzeugen eines neuen Boot-Images!)

Nun kann wie gewohnt die Boot-Liste umgestellt werden und das System nach dem Update rebootet werden.