2011年8月31日水曜日

perl でオプション解析(Getopt::Std編)

以前、bash スクリプトでオプション解析をする際のテンプレートを書きましたが、perl についても、自分用にテンプレート化してみました。
#!/usr/bin/perl
#
# getopts-template.pl
#

use Getopt::Std ;

sub usage_exit {
        print STDERR "Usage: getopts-template.pl [-a] [-d dir] item1 item2 ...\n" ;
        exit 1 ;
}

print "@ARGV\n" ;       ####DEBUG
{
        local $SIG{__WARN__} = \&usage_exit ;
        if (getopts("ad:h") != 1) {
                usage_exit ;
        }
}

if (defined $opt_h) {
        usage_exit ;
}

$argc = scalar @ARGV ;
print "\$argc=$argc\n" ;        ####DEBUG
print "\@ARGV=@ARGV\n" ;        ####DEBUG
print "\$opt_a=$opt_a\n" ;      ####DEBUG
print "\$opt_d=$opt_d\n" ;      ####DEBUG
要所だけ補足しますと、

__WARN__ の行を書かない場合、未定義オプションを指定すると、Getopt::Std が自分でエラーメッセージ(Unknown option: x)を出してしまいます。
わたしは、この振る舞いは嫌なので、自分で用意した usage_exit を呼ぶようにしました。

もう1点、引数が必要なオプション -d dir について、dir が省略された場合にエラーにするには、getopts の戻り値を見る必要があります。getopts は、エラーが生じると、1 以外の値を返します。

実行結果は、次のようになります。
# ./getopts-template.pl -d dir item1 item2
-d dir item1 item2
$argc=2
@ARGV=item1 item2
$opt_a=
$opt_d=dir
bash の getopts と同じ振る舞いのようで、コマンドラインの後半でオプション指定を書くと、うまく処理できません。うーん、残念です。
# ./getopts-template.pl item1 item2 -d dir
item1 item2 -d dir
$argc=4
@ARGV=item1 item2 -d dir
$opt_a=
$opt_d=

2011-09-01追記
getopt(1) を使うテンプレートも作りました。次の記事も参照ください。
perl でオプション解析(getoptコマンド編)

2012-02-11追記
getopts の場合は、-ll のような複数回の指定を区別できないことを知りました。getopt(1) を使う方法ならば対応できますが、コードが長くなるのがいまひとつ。Getopt::Long を使うと、また、別のイライラに遭遇するようです。。。

2017-01-30追記
久々に、このパターンを CentOS 7 で使ったのですが、何やらごちゃごちゃと余計なメッセージが出ました。
[root@hoge tmp]# ./getopts-template.pl --help           
--help
./getopts-template.pl version [unknown] calling Getopt::Std::getopts (version 1.07 [paranoid]),
running under Perl version 5.16.3.

Usage: getopts-template.pl [-OPTIONS [-MORE_OPTIONS]] [--] [PROGRAM_ARG1 ...]

The following single-character options are accepted:
 With arguments: -d
 Boolean (without arguments): -a -h

Options may be merged together.  -- stops processing of options.
Space is not required between options and their arguments.
  [Now continuing due to backward compatibility and excessive paranoia.
   See 'perldoc Getopt::Std' about $Getopt::Std::STANDARD_HELP_VERSION.]
$argc=0
@ARGV=
$opt_a=
$opt_d=
[root@hoge tmp]# 
これはちょっと余計なお世話と思って、調べたところ、次のように追加の記述を行うことで、出なくなりました。
[root@hoge tmp]# diff -u getopts-template.pl getopts-template.pl-2017-01-30 
--- getopts-template.pl 2017-01-30 22:00:17.954554580 +0900
+++ getopts-template.pl-2017-01-30 2017-01-30 22:02:00.828239598 +0900
@@ -11,6 +11,8 @@
 }
 
 print "@ARGV\n" ; ####DEBUG
+sub HELP_MESSAGE { usage_exit ; }
+sub VERSION_MESSAGE { ; }
 {
  local $SIG{__WARN__} = \&usage_exit ;
  if (getopts("ad:h") != 1) {
[root@hoge tmp]# ./getopts-template.pl-2017-01-30 --help
--help
Usage: getopts-template.pl [-a] [-d dir] item1 item2 ...
[root@hoge tmp]# 
Simple is best. ですね。

2011年8月21日日曜日

シェルスクリプトで排他制御(flock)

シェルスクリプトの一部を排他制御する必要があり、調べたところ、CentOS 5 以降なら、util-linux に含まれる flock コマンドを使えることがわかりました。オンラインマニュアルに典型的な使い方が書いてあり、これは、なかなか素晴らしいと思いました。最初に考えた人は、すごいなと思います。オンラインマニュアルからの抜粋です。
FLOCK(1)                              H. Peter Anvin                              FLOCK(1)

NAME
       flock - Manage locks from shell scripts

SYNOPSIS
       flock [-sxon] [-w timeout] lockfile [-c] command...

       flock [-sxon] [-w timeout] lockdir [-c] command...

       flock [-sxun] [-w timeout] fd

DESCRIPTION
       This utility manages flock(2) locks from within shell scripts or the command line.

       The first and second forms wraps the lock around the executing a command, in a man・
       er similar to su(1) or newgrp(1).  It locks a specified file or  directory,  which
       is created (assuming appropriate permissions), if it does not already exist.

       The  third form is convenient inside shell scripts, and is usually used the follow・
       ng manner:

       (
         flock -s 200
         # ... commands executed under lock ...
       ) 200>/var/lock/mylockfile
...
オンラインマニュアルの例では、サブシェルを起動していますが、次のようにすれば、シェルスクリプト全体をシリアライズすることも可能です。
#!/bin/bash

exec 9>>/var/lock/mylockfile

flock -n 9
if [ $? -ne 0 ] ; then
    # ロックできなければ、失敗(exit 1)させるかどうかは、処理の内容次第です。
    # ロック解放を待ち合わせるのなら、-n オプションを付けなければ良い。
fi

#
# 排他の必要な処理を記述。
#

flock -u 9  # スクリプトに対応するプロセスが終了すれば、
            # カーネルが自動的にロック解放しますが、明示的に記述する。
exit 0
exec 9>>/var/lock/mylockfile という書き方をすると、/var/lock/mylockfile を追記モードで open して、その fd を 9 番に割り当てます。シェル内部では、dup2(2) が使われます。

CentOS 4 以下では、flock コマンドはまだ提供されておらず、lockfile という似たコマンドがあるようです。しかし、lockfile のほうは、原始的なテクニックが使われており、flock の方が信頼性は高いと思われます。

2011-09-04追加
flock コマンドは、flock(2) システムコールに依存しており、NFS では動作しないので注意が必要なようです。オンラインマニュアル (man 2 flock) から抜粋です。
2011-09-08 実験してみたところでは動作しました。追記内容を参照してください。
NOTES
       flock(2)  does  not  lock files over NFS.  Use fcntl(2) instead: that does
       work over NFS, given a sufficiently recent version of Linux and  a  server
       which supports locking.
flock はファイルシステム毎に実装 (struct file operations の flock メンバ) することができ、確かに NFS では、次のように定義されていました。
以下、2.6.18-194.el5 (CentOS 5.5) から抜粋です。
     54 const struct file_operations nfs_file_operations = {
     55         .llseek         = nfs_file_llseek,
     56         .read           = do_sync_read,
     57         .write          = do_sync_write,
     58         .aio_read               = nfs_file_read,
     59         .aio_write              = nfs_file_write,
     60         .mmap           = nfs_file_mmap,
     61         .open           = nfs_file_open,
     62         .flush          = nfs_file_flush,
     63         .release        = nfs_file_release,
     64         .fsync          = nfs_fsync,
     65         .lock           = nfs_lock,
     66         .flock          = nfs_flock,
     67         .sendfile       = nfs_file_sendfile,
     68         .check_flags    = nfs_check_flags,
     69 };
...
    639 /*
    640  * Lock a (portion of) a file
    641  */
    642 static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
    643 {
    644         dprintk("NFS: nfs_flock(f=%s/%ld, t=%x, fl=%x)\n",
    645                         filp->f_dentry->d_inode->i_sb->s_id,
    646                         filp->f_dentry->d_inode->i_ino,
    647                         fl->fl_type, fl->fl_flags);
    648 
    649         /*
    650          * No BSD flocks over NFS allowed.
    651          * Note: we could try to fake a POSIX lock request here by
    652          * using ((u32) filp | 0x80000000) or some such as the pid.
    653          * Not sure whether that would be unique, though, or whether
    654          * that would break in other places.
    655          */
    656         if (!(fl->fl_flags & FL_FLOCK))
    657                 return -ENOLCK;
    658 
"fs/nfs/file.c"
flock(2) システムコールのコードは次のようになっています。
   1590 /**
   1591  *      sys_flock: - flock() system call.
   1592  *      @fd: the file descriptor to lock.
   1593  *      @cmd: the type of lock to apply.
   1594  *
   1595  *      Apply a %FL_FLOCK style lock to an open file descriptor.
   1596  *      The @cmd can be one of
   1597  *
   1598  *      %LOCK_SH -- a shared lock.
   1599  *
   1600  *      %LOCK_EX -- an exclusive lock.
   1601  *
   1602  *      %LOCK_UN -- remove an existing lock.
   1603  *
   1604  *      %LOCK_MAND -- a `mandatory' flock.  This exists to emulate Windows Share Modes.
   1605  *
   1606  *      %LOCK_MAND can be combined with %LOCK_READ or %LOCK_WRITE to allow other
   1607  *      processes read and write access respectively.
   1608  */
   1609 asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
   1610 {
   1611         struct file *filp;
   1612         struct file_lock *lock;
   1613         int can_sleep, unlock;
   1614         int error;
   1615 
   1616         error = -EBADF;
   1617         filp = fget(fd);
   1618         if (!filp)
   1619                 goto out;
   1620 
   1621         can_sleep = !(cmd & LOCK_NB);
   1622         cmd &= ~LOCK_NB;
   1623         unlock = (cmd == LOCK_UN);
   1624 
   1625         if (!unlock && !(cmd & LOCK_MAND) && !(filp->f_mode & 3))
   1626                 goto out_putf;
   1627 
   1628         error = flock_make_lock(filp, &lock, cmd);
   1629         if (error)
   1630                 goto out_putf;
   1631         if (can_sleep)
   1632                 lock->fl_flags |= FL_SLEEP;   1633 
   1634         error = security_file_lock(filp, cmd);
   1635         if (error)
   1636                 goto out_free;
   1637 
   1638         if (filp->f_op && filp->f_op->flock)
   1639                 error = filp->f_op->flock(filp,
   1640                                           (can_sleep) ? F_SETLKW : F_SETLK,
   1641                                           lock);
   1642         else
   1643                 error = flock_lock_file_wait(filp, lock);
...
"fs/locks.c"
NFS の場合は、f_op->flock が定義されているので、1639行目が実行され、最終的に ENOLOCK エラーが返却されることになるようです。2011-09-08 読み誤っていましたので、追記内容を参照してください。
ext3 等の場合は、f_op->flock が未定義なので、1643行目が実行されます。
なお、lock->fl_flags に FL_FLOCK を設定しているのは、1628行目です。
    278 /* Fill in a file_lock structure with an appropriate FLOCK lock. */
    279 static int flock_make_lock(struct file *filp, struct file_lock **lock,
    280                 unsigned int cmd)
    281 {
    282         struct file_lock *fl;
    283         int type = flock_translate_cmd(cmd);
    284         if (type < 0)
    285                 return type;
    286        
    287         fl = locks_alloc_lock();
    288         if (fl == NULL)
    289                 return -ENOMEM;
    290 
    291         fl->fl_file = filp;
    292         fl->fl_pid = current->tgid;
    293         fl->fl_flags = FL_FLOCK;
    294         fl->fl_type = type;
    295         fl->fl_end = OFFSET_MAX;
    296        
    297         *lock = fl;
    298         return 0;
    299 }
"fs/locks.c"

2011-09-08追記
fs/nfs/file.c の 656 行目を条件を読み間違えていました。ここは、FL_FLOCK が設定されていないのに、ここに来た場合に ENOLCK を返すというだけでした。
それで、続きを読んでみると、POSIX のロックを使ってシミュレートするよ とあります。
    639 /*
    640  * Lock a (portion of) a file
    641  */
    642 static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
    643 {
    644         dprintk("NFS: nfs_flock(f=%s/%ld, t=%x, fl=%x)\n",
    645                         filp->f_dentry->d_inode->i_sb->s_id,
    646                         filp->f_dentry->d_inode->i_ino,
    647                         fl->fl_type, fl->fl_flags);
    648 
    649         /*
    650          * No BSD flocks over NFS allowed.
    651          * Note: we could try to fake a POSIX lock request here by
    652          * using ((u32) filp | 0x80000000) or some such as the pid.
    653          * Not sure whether that would be unique, though, or whether
    654          * that would break in other places.
    655          */
    656         if (!(fl->fl_flags & FL_FLOCK))
    657                 return -ENOLCK;
    658 
    659         /* We're simulating flock() locks using posix locks on the server */
    660         fl->fl_owner = (fl_owner_t)filp;
    661         fl->fl_start = 0;
    662         fl->fl_end = OFFSET_MAX;
    663 
    664         if (fl->fl_type == F_UNLCK)
    665                 return do_unlk(filp, cmd, fl);
    666         return do_setlk(filp, cmd, fl);
    667 }
"fs/nfs/file.c"
それじゃあ、flock コマンドが NFS を越えて正しく動作するのかというと、POSIX ロックと BSD ロック (flock) の違い (POSIX ロックは fork を越えられない) により正しく動作しないのでは? っと思い、テストプログラムで確かめてみました。CentOS 5 で確認しています。
#!/bin/bash

exec 9>>./flock.test.lock

flock -n 9
if [ $? -ne 0 ] ; then
        echo "fail" 1>&2
        exit 1
fi

sleep 30

flock -u 9
exit 0
# df -TP .
Filesystem    Type 1024-blocks      Used Available Capacity Mounted on
xx.xx.xx.xx:/ nfs   5921856   5455872    165176      98% /mnt_nfs
# /tmp/flock.sh & /tmp/flock.sh 
[1] 6452
fail
2つ同時起動で、2つ目が失敗してますから、ちゃんとロック出来ているようです。
どうなっているのか理解出来ていませんが、NFS の開発者の方は、flock も視野に実装してくれたということでしょうかね。

2011-09-09追記
CentOS 6 を NFS サーバにして、flock を試すとうまく行かず、次のようなエラーになってしまいました。クライアントは CentOS 5 です。
# ./flock.sh 
flock: 9: No locks available
fail
サーバ側を見てみると、次のエラーが出ていました。
Sep  8 23:39:14 centos6 rpc.statd[6061]: No canonical hostname found for xx.xx.xx.xx
Sep  8 23:39:14 centos6 rpc.statd[6061]: STAT_FAIL to centos6 for SM_MON of xx.xx.xx.xx
Sep  8 23:39:14 centos6 kernel: lockd: cannot monitor xx.xx.xx.xx
調べてみると、次の情報が見つかりました。

http://dfwarden.blogspot.com/2011/02/psa-nfs-locking-in-rhel6-needs-reverse.html

確かに、サーバ側 (CentOS 6) の /etc/hosts に、クライアントの IP アドレスを登録したら、flock 出来るようになりました。信頼性・セキュリティの強化が図られているということでしょうかね。

なお、lockd: cannot monitor のメッセージは、クライアント側で nfslock サービスが上がっていない場合にも出るメッセージなようです。メモ。

2011年8月14日日曜日

Fedora 15 のカーネルを Core2 に最適化する

Linux 3.0 のビルド方法 を書きましたが、もう1歩進んで、カーネルのカスタマイズをしてみます。

初歩的なターゲットと思いますが、、、
Fedora 等のディストリビューションでは、古いマシンでも動くように、CPU 種類毎の最適化は行われないようです。CONFIG_GENERIC_CPU=y になっています。
# grep CONFIG_GENERIC_CPU /boot/config-2.6.40-4.fc15.x86_64 
CONFIG_GENERIC_CPU=y
わたしが今使っている ThinkPad X301 は、Core2 が載っているので、Core2 に最適化してみました。
# make menuconfig
CUI画面になるので、
Processor type and features
  ---> Processor family (Core 2/newer Xeon)
    ---> (X) Core 2/newer Xeon
を選択する。
menuconfig の操作方法は、いたって簡単で、矢印キーで移動、リターンで選択、ESC 2回でサブメニューを抜ける。というものです。
あとは、Fedora 15 で Linux 3.0 の手順(6)以降を行います。

最適化前後で、GRUB からログイン画面までの起動時間を比べてみましたが、、、
残念ながら、わたしの環境では、ほとんど違いはありませんでした。

2011年8月7日日曜日

CentOS 6.0 で NIC と ethX の対応を固定化する

CentOS 6.0 で NIC 交換 (オンボードの場合はマザーボード交換も含む) した場合でも、ethX が変化しないように、固定化する方法です。RHEL6 でも同様にして可能。
2015-01-18追記、CentOS7/RHEL7についてはこちらを参照。

CentOS 6.0 のデフォルトでは、MAC アドレスと ethX を対応付けており、交換により MAC が変化すると、自動的に新たな対応関係が作られるようになっているため、ethX がずれてしまいます。対応関係は、/etc/udev/rules.d/70-persistent-net.rules に設定されます。
このあたりについては、前に書いた記事を参照してください。

CentOS 6.0 で NIC 交換した場合の挙動
CentOS 6.0 で NIC と ethX の対応を変更する

以上が基礎知識ですが、まずは、この 70-persistent-net.rules を無効化します。
少々、荒っぽいやり方ですが、ルールが自動生成されないように、/dev/null へシンボリックリンクを張ってしまいます。
[root@centos6 ~]# ln -s -f /dev/null /etc/udev/rules.d/70-persistent-net.rules
[root@centos6 ~]# ls -l /etc/udev/rules.d/70-persistent-net.rules
lrwxrwxrwx 1 root root 9 Aug  7 12:07 /etc/udev/rules.d/70-persistent-net.rules -> /dev/null
このとき messages に、次のメッセージが出ますが、一時的なものなので、気にする必要はありません。
Aug  7 12:07:22 centos6 udevd[406]: can not read '/etc/udev/rules.d/70-persistent-net.rules'

次に、/etc/udev/rules.d/65-eth.rules ファイルを新規作成して、次のように記載します。
ACTION=="add", KERNEL=="eth*", ID=="0000:02:01.0", DRIVERS=="?*", ATTR{type}=="1", NAME="eth2", OPTIONS="last_rule"
ACTION=="add", KERNEL=="eth*", ID=="0000:02:04.0", DRIVERS=="?*", ATTR{type}=="1", NAME="eth3", OPTIONS="last_rule"
ここで、0000:02:01.0 や 0000:02:04.0 は PCI アドレスです。lspci で表示される値に、0000: (domain と呼ばれる管理番号ですが、普通のマシンは 0000 です) をつけた値です。この PCI アドレスは、ethtool コマンドで確認することもできます。
[root@centos6 ~]# ethtool -i eth2
driver: e1000
version: 7.3.21-k6-NAPI
firmware-version: N/A
bus-info: 0000:02:01.0
[root@centos6 ~]# ethtool -i eth3
driver: e1000
version: 7.3.21-k6-NAPI
firmware-version: N/A
bus-info: 0000:02:04.0
書き忘れそうでしたが、この方法を用いる場合は、/etc/sysconfig/network-scripts/ifcfg-ethX の HWADDR= は設定しないほうが良いです。設定した場合は、MAC のチェックが行われ、一致しなければ ifup が失敗します。

最後に、再起動します。設定内容によっては、立ち上げ時に、ethX の入れ替えを行ったことを示すメッセージが出力されます。
Aug  7 12:45:05 centos6 kernel: udev: renamed network interface eth0 to eth2
Aug  7 12:45:05 centos6 kernel: udev: renamed network interface eth1 to eth3
この環境では、NIC は 2本 (eth0, eth1) なのですが、ここで紹介した PCI アドレスによる固定化方法により、eth2 および eth3 に変更しています。

udev に関して、次の記事を一読されると理解が深まるものと思います。

参考URL:
http://gihyo.jp/dev/serial/01/sc-literacy/0015?page=1
http://donko.jp/LFS/LFS6.4jp/chapter07/network.html

2011-08-12追記
Fedora 15 では、biosdevname が使えるとか。参考メモ。
http://ja.community.dell.com/techcenter/b/weblog/archive/2011/08/03/fedora-15.aspx
2011-12-10追記、RHEL6.1 以降でもbiosdevnameが利用できるようです。DellマシンではデフォルトON、その他ベンダではデフォルトOFF。ブートパラメータとしてbiosdevname={0|1} を渡すことで切り替えも可能。HW が対応している必要があるので、事前に biosdevname コマンド(-i ethXX オプション)で名前解決可能かどうか確かめたほうがいいです。ただ、実際に試してみると、em1 や p1p1 みたいなインタフェース名って気色悪く感じました。あと、おそらく ethXX を決め打ちにしているアプリケーションがありそうなので、まだまだ業務サーバで利用はできないと思います。きっと普及までには5年ぐらいの歳月がかかるのではないかと思う。
なお、ブートパラメータとして指定する biosdevname=1 は、カーネルが解釈するわけではありません。Fedora 15 以降 または RHEL6.1 以降の環境で、/lib/udev/rules.d/71-biosdevname.rules を参照してください。この中から /proc/cmdline を読み取るという仕掛けのようです。/proc/cmdline を伝言板のように使うこの実装は醜いと思うんですがね。


2011-08-17追記
Fedora 15 で、上に書いた 65-eth.rules を使うと、ID= は将来の udev で廃止になるから使うなというようなメッセージが出るようです。
Aug 18 00:20:35 fedora15 udevd[358]: ID= will be removed in a future udev version, please use KERNEL= to match the event device, or KERNELS= to match a parent device, in /etc/udev/rules.d/65-eth.rules:2
そこで、別解です。次のルールであれば、Fedora 15 でも CentOS 6.0 でも使えるようです。
ACTION=="add", SUBSYSTEMS=="pci", KERNELS=="0000:02:01.0", DRIVERS=="?*", NAME="eth2", OPTIONS="last_rule"
ACTION=="add", SUBSYSTEMS=="pci", KERNELS=="0000:02:04.0", DRIVERS=="?*", NAME="eth3", OPTIONS="last_rule"
udev ルールに使える attribute は、次のようにして確認できます。
[root@centos6 ~]# udevadm info -a -p /sys/class/net/eth2

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:11.0/0000:02:01.0/net/eth2':
    KERNEL=="eth2"
    SUBSYSTEM=="net"
    DRIVER==""
    ATTR{addr_assign_type}=="0"
    ATTR{addr_len}=="6"
    ATTR{dev_id}=="0x0"
    ATTR{ifalias}==""
    ATTR{iflink}=="2"
    ATTR{ifindex}=="2"
    ATTR{features}=="0x10ba9"
    ATTR{type}=="1"
    ATTR{link_mode}=="0"
    ATTR{address}=="00:0c:29:xx:xx:xx"
    ATTR{broadcast}=="ff:ff:ff:ff:ff:ff"
    ATTR{carrier}=="1"
    ATTR{speed}=="1000"
    ATTR{duplex}=="full"
    ATTR{dormant}=="0"
    ATTR{operstate}=="up"
    ATTR{mtu}=="1500"
    ATTR{flags}=="0x1003"
    ATTR{tx_queue_len}=="1000"

  looking at parent device '/devices/pci0000:00/0000:00:11.0/0000:02:01.0':
    KERNELS=="0000:02:01.0"
    SUBSYSTEMS=="pci"
    DRIVERS=="e1000"
    ATTRS{vendor}=="0x8086"
    ATTRS{device}=="0x100f"
    ATTRS{subsystem_vendor}=="0x15ad"
    ATTRS{subsystem_device}=="0x0750"
    ATTRS{class}=="0x020000"
    ATTRS{irq}=="19"
    ATTRS{local_cpus}=="3"
    ATTRS{local_cpulist}=="0-1"
    ATTRS{modalias}=="pci:v00008086d0000100Fsv000015ADsd00000750bc02sc00i00"
    ATTRS{numa_node}=="-1"
    ATTRS{enable}=="1"
    ATTRS{broken_parity_status}=="0"
    ATTRS{msi_bus}==""

  looking at parent device '/devices/pci0000:00/0000:00:11.0':
    KERNELS=="0000:00:11.0"
    SUBSYSTEMS=="pci"
    DRIVERS==""
    ATTRS{vendor}=="0x15ad"
    ATTRS{device}=="0x0790"
    ATTRS{subsystem_vendor}=="0x0000"
    ATTRS{subsystem_device}=="0x0000"
    ATTRS{class}=="0x060401"
    ATTRS{irq}=="0"
    ATTRS{local_cpus}=="3"
    ATTRS{local_cpulist}=="0-1"
    ATTRS{modalias}=="pci:v000015ADd00000790sv00000000sd00000000bc06sc04i01"
    ATTRS{numa_node}=="-1"
    ATTRS{enable}=="1"
    ATTRS{broken_parity_status}=="0"
    ATTRS{msi_bus}=="1"

  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""

2015-01-16追記
RHEL7/CentOS7 の場合について、別の記事を書きました。

2011年8月6日土曜日

CentOS の bonding の arp_validate オプション

CentOS 5 および CentOS 6 の bonding で ARP 監視を行う場合、arp_validate というオプションを利用できます。
以下、その処理内容についてのメモです。

 Bladeサーバ            Blade収納ユニット
┌──────────┐──────────────┐
│              active│          ┌───┐Blade   │   Blade外部switch
│    ┌──┐  ┌──┤          │      │内部switch  ┌───┐
│    │    ├─┤eth0├─────┤ SW1  ├─────┤      │        
│    │    │  └──┤→…………………………………………┐    │  ┌───┐
│    bond0 │        │ARP要求   └───┘        │  │SW3 │  arp_ip_target
│    │192.168.50.111│broadcast ┌───┐        │  │    ├─┤192.168.50.222
│    │    │  ┌──┤←…………………………………………┴………→│      │
│    │    ├─┤eth1├─────┤ SW2  ├──────┤      │  └───┘
│    └──┘  └──┤          │      │        │  └───┘
│              backup│          └───┘        │
└──────────┘──────────────┘

Bladeサーバの場合、上図のように、収納ユニット内部にスイッチ (SW1 および SW2) が実装される構造になっていることがあります。この場合、部分の障害を検出できるように ARP 監視を用いることができます。
注記:内部スイッチが、部分のリンク障害時に、eth0---SW1 間のリンクを無効化するという機能(UFD: Uplink Failure Detection)を備えている場合があります。機能があるなら、併用するのがいいのではと思います。スイッチの障害対応機能が完璧に動くかどうか?という側面もあるため、スイッチ機能+MII 監視ではなく、スイッチ機能+ARP 監視が良いものと思います。
ただし、ARP 監視のデフォルト動作 (arp_validate=0) では、受信カウンタのチェックのみ行われるため、隣のBladeサーバが同様に ARP 監視 (ARP要求のbroadcast送信) を行ってしまうと、正しく障害検知出来なくなります。

arp_validate=3 を設定すると、受信した ARP パケットの正当性を確認する処理が動くようになります。active (上図eth0) では、arp_ip_target からの ARP 応答を受信したかどうかの確認が行われるようになります。一方、わかりにくいところですが、backup (上図eth1) では、active (上図eth0) から送信された ARP 要求 (broadcast) を受信したかどうかの確認が行われるようになります。ドライバソース上、この ARP 確認処理の核心部分は、drivers/net/bonding/bond_main.c の次の箇所です。
   2741 static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
   2742 {
...
   2789         /*
   2790          * Backup slaves won't see the ARP reply, but do come through
   2791          * here for each ARP probe (so we swap the sip/tip to validate
   2792          * the probe).  In a "redundant switch, common router" type of
   2793          * configuration, the ARP probe will (hopefully) travel from
   2794          * the active, through one switch, the router, then the other
   2795          * switch before reaching the backup.
   2796          */
   2797         if (slave->state == BOND_STATE_ACTIVE)
   2798                 bond_validate_arp(bond, slave, sip, tip);
   2799         else
   2800                 bond_validate_arp(bond, slave, tip, sip);
   2801
...
"drivers/net/bonding/bond_main.c"
2798行目が、active 側の確認 (arp_ip_target からの ARP 応答であるか?) です。
2800行目が、backup 側の確認 (arp_ip_target に対する ARP 要求であるか?) です。
掲載したソースは、2.6.18-164.el5 (CentOS 5.5 のカーネル) のものですが、2.6.32-71.el6 (CentOS 6.0 のカーネル) でも、全く同じです。

最後に、この記事の図に対応する設定例サンプル (/etc/sysconfig/network-scripts/ifcfg-bond0, ifcfg-eth0, ifcfg-eth1) を載せます。
#
# network-scripts/ifcfg-bond0
#
DEVICE=bond0
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.50.111
NETMASK=255.255.255.0
GATEWAY=192.168.50.254
BONDING_OPTS="mode=1 arp_interval=1200 arp_ip_target=192.168.50.222 arp_validate=3"
USERCTL=no
NM_CONTROLLED=no
arp_interval=1200 ミリ秒を勧めます。sysctl の net.ipv4.neigh.default.locktime (単位10ミリ秒) よりも長い値。
#
# network-scripts/ifcfg-eth0
#
DEVICE=eth0
MASTER=bond0
SLAVE=yes
BOOTPROTO=none
HWADDR=00:0C:29:xx:xx:xx
USERCTL=no
NM_CONTROLLED=no
#
# network-scripts/ifcfg-eth1
#
DEVICE=eth1
MASTER=bond0
SLAVE=yes
BOOTPROTO=none
HWADDR=00:0C:29:yy:yy:yy
USERCTL=no
NM_CONTROLLED=no

2011-08-08追記
どうやら Internet Explorer だと、テキストで書いた図が歪むようですので、PNG を貼ります。Chrome と Firefox ではきれいに見えます。IE の方は試してみては。

2011年8月5日金曜日

Fedora 15 で Linux 3.0

Fedora 15 の最新カーネル 2.6.40-4.fc15 は、実質は Linux 3.0 です。
rpm の changelog に次のようにあります。
# rpm -q --changelog kernel-2.6.40-4.fc15 | head -14
* Fri Jul 29 2011 Dave Jones  2.6.40-4
- Re-add utrace, which got accidentally dropped during the rebase.

* Thu Jul 28 2011 Dave Jones  2.6.40-3
- Fix module-init-tools conflict:

* Thu Jul 28 2011 Dave Jones  2.6.40-2
- fix crash in scsi_dispatch_cmd()

* Thu Jul 28 2011 Dave Jones  2.6.40-1
- Turn off debugging options. (make release)

* Tue Jul 26 2011 Dave Jones  2.6.40-0
- Rebase to final 3.0 (munge to 2.6.40-0)
おそらく、カーネルのバージョンにより処理内容を変えるアプリケーションへの配慮のためか、2.6.40 ということにしてあります。
以下、この 2.6.40-4.fc15 に含まれる config を使って、Linux 3.0 をビルドする手順です。バージョン表示だけのことではありますが、新鮮な気分を味わえます。

(1) Fedora 15 の動作環境を用意して、カーネル 2.6.40-4.fc15 までアップデートする。ただし、ビルドのためには、空き領域が 8GB 程度必要です。

(2) 開発用ツールをインストールする。
# yum groupinstall "Development Tools"

(3) http://www.kernel.org/ から linux-3.0.tar.bz2 を入手して展開する。
# cd /usr/src  ※これは例です。ほかの場所でも良い
# tar xf /tmp/linux-3.0.tar.bz2

(4) Makefile の EXTRAVERSION を設定する。
VERSION = 3
PATCHLEVEL = 0
SUBLEVEL = 0
EXTRAVERSION = -0.my15.x86_64  ※これは例です。自分の好みの文字列を指定すれば良い
...

(5) 2.6.40-4.fc15 の config をコピーする。
# cd /usr/src/linux-3.0
# cp /boot/config-2.6.40-4.fc15.x86_64 .config

(6) ビルドを実行する。もしも、2.6.38.8-35.fc15 等の少し古い config を用いた場合、新しく増えたオプションをどう設定するか問い合わせて来ます。2.6.40-4.fc15 であれば、オプション集合が一致しているので、何も聞かれることはなく、そのままコンパイルが進行します。
# make HOSTCXX="ccache g++" CC="ccache gcc"

(7) 出来上がったモジュールとカーネルをインストールする。
# make modules_install
# make install

(8) grub.conf の default を修正して再起動する。または、再起動時に GRUB メニューから作成した Linux 3.0 のエントリーを選択して起動する。

(9) uname -a で、カーネルバージョンを確認する!
# uname -a
Linux fedora15 3.0.0-0.my15.x86_64 #1 SMP Thu Aug 4 05:21:08 JST 2011 x86_64 x86_64 x86_64 GNU/Linux

2011-08-06追記
2.6.40-4.fc15 のアナウンス内容です。
http://lists.fedoraproject.org/pipermail/package-announce/2011-August/063271.html
Rebase to 3.0. Version reports as 2.6.40 for compatibility with older userspace.

だそうです。

2011-08-07追記
Linux のバージョン番号と歴史について、次の記事にまとめられています。
http://gihyo.jp/lifestyle/serial/01/ganshiki-soushi/0023

2011-08-09追記
手順 (6) で、make の並列化オプション --jobs=N (N は core 数 x 2 程度の自然数) を指定すると、ビルド時間を短縮できるようです。手元の 2core の環境で、--jobs=4 を指定したところ、確かにコンパイル時間が半分程度で済みました。

2011-08-14追記
ccache についての解説が下記にあります。
http://www.ibm.com/developerworks/jp/linux/library/l-ccache/

2011-09-09追記
Fedora 16 Alpha が出ており、そちらのカーネルは既に 3.0 になっているので、ビルドまではしたくない方は、そちらを試してみては。
人気ブログランキングへ にほんブログ村 IT技術ブログへ