VMware におけるSCSIコントローラの認識順序

VMwareSCSIコントローラはその名前の順番通りにはOSに認識される保証が無い、というちょっとしたショックから調べてみたこと。
なお、検証に使用した環境は VMware ESXi 5.0.0 と CentOS 6.4 x86_64 である。

背景事情

Linuxのデバイス名決定方式

LinuxにおいてはNICSCSI接続のディスクは「認識順」で番号付けされる。NICであれば認識順にeth0, eth1,・・・であり、SCSI接続のディスクならばsda, sdb,・・・である。

NICについてはudevを使用することで特定のMACアドレスを持つカード・ポートを常に同じethの番号に対応づけることができる(正確には、認識後にudevの力でリネームしている)。ところがSCSI接続のディスクについてはudevを使用してもsdaやsdbに特定のディスクを強制的に結びつけることができない・・・調べた限りでは。udevではあくまでsdaやsdbとは異なる別名を与える事で固定化する方法が紹介されている。

2015/1/18追記
udevで/dev/sdaや/dev/sdbを特定のディスクに結びつけることは可能であった(sdaやsdbではなく/dev/sdaや/dev/sdbとしていることに理由がある)。ただしethほど綺麗にはできないため、使うのは躊躇われる。詳細は別途書く(udevを使用したディスクの固定化)。
VMwareSCSI接続のディスク

VMwareでは仮想マシンSCSI接続のディスク(VMDKやRDMなどのディスク)を接続する為に「SCSIコントローラ」が用意されている。SCSIコントローラは最大4個接続でき、SCSIコントローラ0から3までの番号付けとなる。

SCSIコントローラには15個のディスクを接続でき、接続するディスクはSCSIコントローラ0の1番目(0:0)、SCSIコントローラ1の3番目(1:2)などと選択できる。ここでポイントになるのは、以下の事実である。

  • 同じSCSIコントローラcに接続する場合、n<mにおいて、(c:n)に接続するディスクは常に(c:m)に接続するディスクより先に認識される。
  • x<yにおいて、SCSIコントローラxがSCSIコントローラyより先に認識される保証は無い。

(2個のSCSIコントローラを接続している構成)

(ハードディスク2をSCSIコントローラ1の最初のデバイス(SCSI (1:0))として接続)

事実関係

SCSIコントローラを仮想マシンに追加すると、vmxファイルには「scsi0.pciSlotNumber = "160"」といった設定が入る。名前の通り、これがPCIバイスの接続位置を左右しており、これがOSのSCSIコントローラの認識順を決定している。ではこのpciSlotNumberの大小関係がSCSIコントローラの番号の大小関係と一致していれば直感に反しない認識順となるのか?これがならない。

pciSlotNumberの解釈方法

pciSlotNumberの解釈方法については下記ページで説明されている。10進数として解釈するのではなく、13桁の2進数として表記し、上位3桁、続く5桁、最後の5桁、それぞれの意味を解釈しなければならない。

Mapping PCI slot numbers to guest-visible PCI bus topology (2047927)

これによれば、「160」(10進数)は、以下の通りに解釈される。

          FFF BBBBB DDDDD
160[10] = 000 00101 00000[2]

  FFF   =   000[2] = 0[10]
  BBBBB = 00101[2] = 5[10]
  DDDDD = 00000[2] = 0[10]

こうして得られる FFF, BBBBB, DDDDD の解釈は2通りになる。

  1. BBBBB が 00000 である場合、当該のデバイスは Bus 0 に接続している。そして、OSに Bus:Device.Function = 0:DDDDD.0 として認識される。
  2. BBBBB が 00000 ではない場合、当該のデバイスは Bus 0 に接続しておらず、PCIブリッジにより接続された Bus N (N > 0) に接続している。そして、OSに Bus:Device.Function = N:DDDDD.0 として認識される。
    • NはPCIブリッジによりどのような接続をされているかに応じて、各 Bus が与えられる番号であり、BBBBB から直接は求められない。ただし、BBBBB は、どのPCIブリッジにより上位の Bus と接続しているのかの情報を与えている。

要するに、DDDDD は Bus:Device.Function の Device を表しており、BBBBB は Bus・ブリッジ に関する情報を与えている。

PCIのBusとPCIブリッジの考え方

PCIのBusとPCIブリッジ(PCI bridge)の説明用の図を示す。

  • PCIバイスPCIのBusに接続する。
  • PCIのBusはまず基本となる Bus 00 が存在する。
    • Bus 00 に直接接続するPCIバイスが存在する(前述の BBBBB が 00000 の場合はこれにあたる)。上記図では 00:0F.0 として認識されているデバイスがそれである。Bus 00 に接続しているPCIブリッジもデバイスとして見なせば同様である。
  • PCIブリッジを経由して別のPCIのBusが接続されている。
    • PCIのBusはBus 00以外は認識順(Device.Functionの小さい順)に 01, 02, 03 ・・・ と番号付けされる(深さ優先で認識される)。
    • PCIブリッジの上下それぞれのPCIのBusの番号は 「 lspci -v 」コマンドの出力で、「Bus: primary=xx, secondary=yy」(xxが上、yyが下)として表示されるためOSから調べる事ができる。
    • 上記図の00:15に接続しているPCIブリッジは1デバイスの8機能(Function)それぞれがPCIブリッジの機能を果たすため、8個のPCIブリッジとして機能する。

ここで上記図の 00:0F.0 に接続しているデバイスの pciSlotNumber を逆算すると、以下のようになる。

  1. 00:0F[16進数].0 = 0:DDDDD[2進数].0 であるため、DDDDD=01111
  2. Bus 00 に直接接続しているため BBBBB=00000, FFF=000
  3. よって FFFBBBBBDDDDD=0000000001111[2進数]=15[10進数]
  4. つまり pciSlotNumber="15" である。

同様に 00:15 に接続しているPCIブリッジの pciSlotNumber を逆算すると、以下のようになる。

  1. 00:15[16進数].0 = 0:DDDDD[2進数].0 であるため、DDDDD=10101
  2. Bus 00 に直接接続しているため BBBBB=00000, FFF=000
  3. よって FFFBBBBBDDDDD=0000000010101[2進数]=21[10進数]
  4. つまり pciSlotNumber="21" である。

なお、このPCIブリッジの pciSlotNumber は vmx ファイル上では次のように記載されている。

pciBridge4.pciSlotNumber = "21"

最後に 0B:00.0 に接続しているデバイスの pciSlotNumber であるが、これは「7328」である。理由は「7328」が以下のように解釈されるからだ。

           FFF BBBBB DDDDD
7328[10] = 111 00101 00000[2]

  FFF   =   111[2] = 7[10] → 上位のBusと接続するPCIブリッジのデバイスの Function 7 (00:15.7)に接続しているため。
  BBBBB = 00101[2] = 5[10] → 上位のBusと接続するPCIブリッジのデバイスが pciBridge「4」であるため(BBBBBの値から1を引くことで vmxファイル上のPCIブリッジの番号と対応づけられる)。
  DDDDD = 00000[2] = 0[10] → 0B:00.0 であり Device 00 = DDDDD であるため。

PCIバイスの pciSlotNumber と上位のBusと接続するPCIブリッジの番号、同ブリッジの pciSlotNumber の関係を示す(PCIブリッジは Bus 00 に接続する前提)。

実際の仮想マシンにおける認識順の検証

これまでの理解をもとに実際の仮想マシンにおける認識順を検証する。

仮想マシン設定

SCSIコントローラ1と2にはそれぞれディスクを1個ずつ接続する。認識順を明確に区別するため、それぞれ1GBと2GBで作成している。

vmxファイルからの抜粋
pciBridge0.pciSlotNumber = "17"
pciBridge4.pciSlotNumber = "21"
pciBridge5.pciSlotNumber = "22"
pciBridge6.pciSlotNumber = "23"
pciBridge7.pciSlotNumber = "24"

ethernet0.pciSlotNumber = "192"
ethernet1.pciSlotNumber = "224"
ethernet2.pciSlotNumber = "1216"
ethernet3.pciSlotNumber = "1248"

scsi0.pciSlotNumber = "160"
scsi1.pciSlotNumber = "256"
scsi2.pciSlotNumber = "1184"

vmci0.pciSlotNumber = "32"

ethernet0.generatedAddress = "00:0c:29:d2:b6:83"
ethernet1.generatedAddress = "00:0c:29:d2:b6:8d"
ethernet2.generatedAddress = "00:0c:29:d2:b6:97"
ethernet3.generatedAddress = "00:0c:29:d2:b6:a1"
lspci実行結果
# lspci -v | egrep -e '..:..\..' -e 'Bus:'

00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)
00:01.0 PCI bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=01, subordinate=01, sec-latency=64
00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 08)
00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01) (prog-if 8a [Master SecP PriP])
00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)
00:07.7 System peripheral: VMware Virtual Machine Communication Interface (rev 10)
00:0f.0 VGA compatible controller: VMware SVGA II Adapter (prog-if 00 [VGA controller])
00:11.0 PCI bridge: VMware PCI bridge (rev 02) (prog-if 01 [Subtractive decode])
	Bus: primary=00, secondary=02, subordinate=02, sec-latency=68
00:15.0 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=03, subordinate=03, sec-latency=0
00:15.1 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=04, subordinate=04, sec-latency=0
00:15.2 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=05, subordinate=05, sec-latency=0
00:15.3 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=06, subordinate=06, sec-latency=0
00:15.4 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=07, subordinate=07, sec-latency=0
00:15.5 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=08, subordinate=08, sec-latency=0
00:15.6 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=09, subordinate=09, sec-latency=0
00:15.7 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=0a, subordinate=0a, sec-latency=0
00:16.0 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=0b, subordinate=0b, sec-latency=0
00:16.1 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=0c, subordinate=0c, sec-latency=0
00:16.2 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=0d, subordinate=0d, sec-latency=0
00:16.3 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=0e, subordinate=0e, sec-latency=0
00:16.4 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=0f, subordinate=0f, sec-latency=0
00:16.5 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=10, subordinate=10, sec-latency=0
00:16.6 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=11, subordinate=11, sec-latency=0
00:16.7 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=12, subordinate=12, sec-latency=0
00:17.0 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=13, subordinate=13, sec-latency=0
00:17.1 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=14, subordinate=14, sec-latency=0
00:17.2 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=15, subordinate=15, sec-latency=0
00:17.3 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=16, subordinate=16, sec-latency=0
00:17.4 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=17, subordinate=17, sec-latency=0
00:17.5 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=18, subordinate=18, sec-latency=0
00:17.6 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=19, subordinate=19, sec-latency=0
00:17.7 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=1a, subordinate=1a, sec-latency=0
00:18.0 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=1b, subordinate=1b, sec-latency=0
00:18.1 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=1c, subordinate=1c, sec-latency=0
00:18.2 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=1d, subordinate=1d, sec-latency=0
00:18.3 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=1e, subordinate=1e, sec-latency=0
00:18.4 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=1f, subordinate=1f, sec-latency=0
00:18.5 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=20, subordinate=20, sec-latency=0
00:18.6 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=21, subordinate=21, sec-latency=0
00:18.7 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
	Bus: primary=00, secondary=22, subordinate=22, sec-latency=0
03:00.0 Serial Attached SCSI controller: VMware PVSCSI SCSI Controller (rev 02)
04:00.0 Serial Attached SCSI controller: VMware PVSCSI SCSI Controller (rev 02)
0b:00.0 Ethernet controller: VMware VMXNET3 Ethernet Controller (rev 01)
0c:00.0 Ethernet controller: VMware VMXNET3 Ethernet Controller (rev 01)
13:00.0 Ethernet controller: VMware VMXNET3 Ethernet Controller (rev 01)
14:00.0 Ethernet controller: VMware VMXNET3 Ethernet Controller (rev 01)
1b:00.0 Serial Attached SCSI controller: VMware PVSCSI SCSI Controller (rev 02)
ディスク認識順の確認

/dev/sdbが2GB、/dev/sdcが1GBとなっており、SCSIコントローラの認識順は以下のとおりである。いきなり直感に反している。

  1. 【scsi0】SCSIコントローラ0
  2. 【scsi2】SCSIコントローラ2
  3. 【scsi1】SCSIコントローラ1
# fdisk -l

Disk /dev/sda: 42.9 GB, 42949672960 bytes
64 heads, 32 sectors/track, 40960 cylinders
Units = cylinders of 2048 * 512 = 1048576 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00034b9d

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           2         501      512000   83  Linux
Partition 1 does not end on cylinder boundary.
/dev/sda2             502       40960    41430016   8e  Linux LVM
Partition 2 does not end on cylinder boundary.

Disk /dev/sdb: 2147 MB, 2147483648 bytes
67 heads, 62 sectors/track, 1009 cylinders
Units = cylinders of 4154 * 512 = 2126848 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

Disk /dev/sdc: 1073 MB, 1073741824 bytes
34 heads, 61 sectors/track, 1011 cylinders
Units = cylinders of 2074 * 512 = 1061888 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
NIC認識順の確認

ifconfig -a の結果ではネットワークアダプタの接続順になっていることがMACアドレスからわかる。しかしこれはudevによる入れ替え後の状態であるため、実際の認識順は以下のとおりである。これも直感に反している。

  1. 【ethernet0】ネットワークアダプタ1 (MAC=00:0c:29:d2:b6:83)
  2. 【ethernet2】ネットワークアダプタ3 (MAC=00:0c:29:d2:b6:97)
  3. 【ethernet1】ネットワークアダプタ2 (MAC=00:0c:29:d2:b6:8d)
  4. 【ethernet3】ネットワークアダプタ4 (MAC=00:0c:29:d2:b6:a1)

udev によるリネームは、認識直後の eth1 と eth2 を入れ替えるものであり、dmesg 上で確認できる。
ifconfg -a 結果、udev設定、dmesgのログをそれぞれ示す。

# ifconfig -a | grep ^eth
eth0      Link encap:Ethernet  HWaddr 00:0C:29:D2:B6:83  
eth1      Link encap:Ethernet  HWaddr 00:0C:29:D2:B6:8D  
eth2      Link encap:Ethernet  HWaddr 00:0C:29:D2:B6:97  
eth3      Link encap:Ethernet  HWaddr 00:0C:29:D2:B6:A1  

# cat /etc/udev/rules.d/70-persistent-net.rules | grep eth
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:0c:29:d2:b6:83", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:0c:29:d2:b6:8d", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:0c:29:d2:b6:97", ATTR{type}=="1", KERNEL=="eth*", NAME="eth2"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:0c:29:d2:b6:a1", ATTR{type}=="1", KERNEL=="eth*", NAME="eth3"

# dmesg | grep -i eth
eth0: NIC Link is Up 10000 Mbps
eth1: NIC Link is Up 10000 Mbps
eth2: NIC Link is Up 10000 Mbps
eth3: NIC Link is Up 10000 Mbps
udev: renamed network interface eth1 to rename3
udev: renamed network interface eth2 to eth1
udev: renamed network interface rename3 to eth2
PCIの接続状態を図示

vmxファイルのpciSlotNumberとブリッジの情報から接続状態を導き出し、lspciの結果で裏を取る。接続状態は以下の通りである。

pciBridge0.pciSlotNumber = "17"  → 000 00000 10001 → (0,0,17) → Bus 00 直接接続の Device 11 (17[10進数]=11[16進数])
pciBridge4.pciSlotNumber = "21"  → 000 00000 10101 → (0,0,21) → Bus 00 直接接続の Device 15 (21[10進数]=15[16進数])
pciBridge5.pciSlotNumber = "22"  → 000 00000 10110 → (0,0,22) → Bus 00 直接接続の Device 16 (22[10進数]=16[16進数])
pciBridge6.pciSlotNumber = "23"  → 000 00000 10111 → (0,0,23) → Bus 00 直接接続の Device 17 (23[10進数]=17[16進数])
pciBridge7.pciSlotNumber = "24"  → 000 00000 11000 → (0,0,24) → Bus 00 直接接続の Device 18 (24[10進数]=18[16進数])

scsi0.pciSlotNumber = "160"      → 000 00101 00000 → (0,5,0)  → pciBridge4 の Func 0下の Bus の Device 00
scsi1.pciSlotNumber = "256"      → 000 01000 00000 → (0,8,0)  → pciBridge7 の Func 0下の Bus の Device 00
scsi2.pciSlotNumber = "1184"     → 001 00101 00000 → (1,5,0)  → pciBridge4 の Func 1下の Bus の Device 00
ethernet0.pciSlotNumber = "192"  → 000 00110 00000 → (0,6,0)  → pciBridge5 の Func 0下の Bus の Device 00
ethernet1.pciSlotNumber = "224"  → 000 00111 00000 → (0,7,0)  → pciBridge6 の Func 0下の Bus の Device 00
ethernet2.pciSlotNumber = "1216" → 001 00110 00000 → (1,6,0)  → pciBridge5 の Func 1下の Bus の Device 00
ethernet3.pciSlotNumber = "1248" → 001 00111 00000 → (1,7,0)  → pciBridge6 の Func 1下の Bus の Device 00

Bus 00 の番号が小さい Device から認識していく(同じ Device の中では番号が小さい Function を先に認識する)と、以下の順番で認識していることになる。

  1. 【scsi0】SCSIコントローラ0 (1ディスク接続) [pciSlotNumber = "160"]
  2. 【scsi2】SCSIコントローラ2 (1ディスク接続 2GB) [pciSlotNumber = "1184"]
  3. 【ethernet0】ネットワークアダプタ1 (MAC=00:0c:29:d2:b6:83) [pciSlotNumber = "192"]
  4. 【ethernet2】ネットワークアダプタ3 (MAC=00:0c:29:d2:b6:97) [pciSlotNumber = "1216"]
  5. 【ethernet1】ネットワークアダプタ2 (MAC=00:0c:29:d2:b6:8d) [pciSlotNumber = "224"]
  6. 【ethernet3】ネットワークアダプタ4 (MAC=00:0c:29:d2:b6:a1) [pciSlotNumber = "1248"]
  7. 【scsi1】SCSIコントローラ1 (1ディスク接続 1GB) [pciSlotNumber = "256"]

直感に反するSCSIコントローラやNICの認識順も vmxファイル上の pciSlotNumber に基づく接続状態からすれば、当然の結果であったのだ。

各デバイスの接続位置確認

/sys (Sysfs) を確認することで、sdaやeth0の接続位置を確認できる(lspciではあくまでSCSIコントローラとかNICというレベル)。

# ls -l /sys/class/block | grep -w sd.
lrwxrwxrwx. 1 root root 0 Jan 18 08:45 sda -> ../../devices/pci0000:00/0000:00:15.0/0000:03:00.0/host2/target2:0:0/2:0:0:0/block/sda
lrwxrwxrwx. 1 root root 0 Jan 18 08:45 sda1 -> ../../devices/pci0000:00/0000:00:15.0/0000:03:00.0/host2/target2:0:0/2:0:0:0/block/sda/sda1
lrwxrwxrwx. 1 root root 0 Jan 18 08:45 sda2 -> ../../devices/pci0000:00/0000:00:15.0/0000:03:00.0/host2/target2:0:0/2:0:0:0/block/sda/sda2
lrwxrwxrwx. 1 root root 0 Jan 18 08:45 sdb -> ../../devices/pci0000:00/0000:00:15.1/0000:04:00.0/host3/target3:0:0/3:0:0:0/block/sdb
lrwxrwxrwx. 1 root root 0 Jan 18 08:45 sdc -> ../../devices/pci0000:00/0000:00:18.0/0000:1b:00.0/host4/target4:0:0/4:0:0:0/block/sdc

# ls -l /sys/class/net | grep -w eth.
lrwxrwxrwx. 1 root root 0 Jan 18 08:45 eth0 -> ../../devices/pci0000:00/0000:00:16.0/0000:0b:00.0/net/eth0
lrwxrwxrwx. 1 root root 0 Jan 18 08:45 eth1 -> ../../devices/pci0000:00/0000:00:17.0/0000:13:00.0/net/eth1
lrwxrwxrwx. 1 root root 0 Jan 18 08:46 eth2 -> ../../devices/pci0000:00/0000:00:16.1/0000:0c:00.0/net/eth2
lrwxrwxrwx. 1 root root 0 Jan 18 08:45 eth3 -> ../../devices/pci0000:00/0000:00:17.1/0000:14:00.0/net/eth3
検証結果

vmxファイルのpciSlotNumberとブリッジの情報から導き出される接続状態に基づき認識順が決定していることが確認できた。

結論!

  • バイスの認識順は仮想マシンの設定上の番号(SCSIコントローラ1の「1」など)通りではない。
  • 各デバイスが持っている pciSlotNumber に基づきデバイスの接続位置が決定されており、認識順はそれに依存する。
  • pciSlotNumber の大小比較のみでは認識順は決定せず、2進数に分解した上で 3桁・5桁・5桁 それぞれの意味を解釈する必要がある。
  • vmxファイルの pciSlotNumber さえ確認すれば、仮想マシンを起動しなくても認識順の答えは得られる。

pciSlotNumber の直接編集による認識順変更の検証

vmxファイルを直接編集して認識順を直感通りにしてみる。なお編集は仮想マシン設定画面の[Options]-[Advanced]-[General]-[Configuration Parameters...]から行う(SSHで入るなりして編集するしかないと思ってましたが違いました(汗))。

変更前の認識順およびvmxファイル設定
  1. 【scsi0】SCSIコントローラ0 (1ディスク接続) [pciSlotNumber = "160"]
  2. 【scsi2】SCSIコントローラ2 (1ディスク接続 2GB) [pciSlotNumber = "1184"]
  3. 【ethernet0】ネットワークアダプタ1 (MAC=00:0c:29:d2:b6:83) [pciSlotNumber = "192"]
  4. 【ethernet2】ネットワークアダプタ3 (MAC=00:0c:29:d2:b6:97) [pciSlotNumber = "1216"]
  5. 【ethernet1】ネットワークアダプタ2 (MAC=00:0c:29:d2:b6:8d) [pciSlotNumber = "224"]
  6. 【ethernet3】ネットワークアダプタ4 (MAC=00:0c:29:d2:b6:a1) [pciSlotNumber = "1248"]
  7. 【scsi1】SCSIコントローラ1 (1ディスク接続 1GB) [pciSlotNumber = "256"]
scsi0.pciSlotNumber = "160"
scsi1.pciSlotNumber = "256"
scsi2.pciSlotNumber = "1184"
ethernet0.pciSlotNumber = "192"
ethernet1.pciSlotNumber = "224"
ethernet2.pciSlotNumber = "1216"
ethernet3.pciSlotNumber = "1248"
変更後の想定認識順およびvmxファイル設定

SCSIコントローラ内、ネットワークアダプタ内で希望の認識順になるように、pciSlotNumberを交換する。意図した接続のためにpciSlotNumberを「計算(作成)」することもできるかもしれないが、今得られているpciSlotNumberを入れ替えるほうが安心だと考えた。

  1. 【scsi0】SCSIコントローラ0 (1ディスク接続) [pciSlotNumber = "160"]
  2. 【scsi1】SCSIコントローラ1 (1ディスク接続 1GB) [pciSlotNumber = "1184"]
  3. 【ethernet0】ネットワークアダプタ1 (MAC=00:0c:29:d2:b6:83) [pciSlotNumber = "192"]
  4. 【ethernet1】ネットワークアダプタ2 (MAC=00:0c:29:d2:b6:8d) [pciSlotNumber = "1216"]
  5. 【ethernet2】ネットワークアダプタ3 (MAC=00:0c:29:d2:b6:97) [pciSlotNumber = "224"]
  6. 【ethernet3】ネットワークアダプタ4 (MAC=00:0c:29:d2:b6:a1) [pciSlotNumber = "1248"]
  7. 【scsi2】SCSIコントローラ2 (1ディスク接続 2GB) [pciSlotNumber = "256"]
scsi0.pciSlotNumber = "160"
scsi1.pciSlotNumber = "1184"
scsi2.pciSlotNumber = "256"
ethernet0.pciSlotNumber = "192"
ethernet1.pciSlotNumber = "1216"
ethernet2.pciSlotNumber = "224"
ethernet3.pciSlotNumber = "1248"

(ethernet1とethernet2のpciSlotNumber書き換え)

(scsi1とscsi2のpciSlotNumber書き換え)

変更後の起動結果

vmxファイル編集後に起動してみたところ、希望通りの結果となった。認識順を強制的に変更することは可能ということになった。

  • /dev/sdbが1GB、/dev/sdcが2GBとなっており、SCSIコントローラの認識順は直感通りである。
  • dmesg 上で eth1 と eth2 を入れ替える処理のログがなくなっており、入れ替えが不要になったことがわかる。
# fdisk -l

Disk /dev/sda: 42.9 GB, 42949672960 bytes
64 heads, 32 sectors/track, 40960 cylinders
Units = cylinders of 2048 * 512 = 1048576 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00034b9d

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           2         501      512000   83  Linux
Partition 1 does not end on cylinder boundary.
/dev/sda2             502       40960    41430016   8e  Linux LVM
Partition 2 does not end on cylinder boundary.

Disk /dev/sdb: 1073 MB, 1073741824 bytes
34 heads, 61 sectors/track, 1011 cylinders
Units = cylinders of 2074 * 512 = 1061888 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

Disk /dev/sdc: 2147 MB, 2147483648 bytes
67 heads, 62 sectors/track, 1009 cylinders
Units = cylinders of 4154 * 512 = 2126848 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

# dmesg | grep eth
eth0: NIC Link is Up 10000 Mbps
eth1: NIC Link is Up 10000 Mbps
eth2: NIC Link is Up 10000 Mbps
eth3: NIC Link is Up 10000 Mbps

# ls -l /sys/class/block | grep -w sd.
lrwxrwxrwx. 1 root root 0 Jan 18 06:51 sda -> ../../devices/pci0000:00/0000:00:15.0/0000:03:00.0/host2/target2:0:0/2:0:0:0/block/sda
lrwxrwxrwx. 1 root root 0 Jan 18 06:51 sda1 -> ../../devices/pci0000:00/0000:00:15.0/0000:03:00.0/host2/target2:0:0/2:0:0:0/block/sda/sda1
lrwxrwxrwx. 1 root root 0 Jan 18 06:51 sda2 -> ../../devices/pci0000:00/0000:00:15.0/0000:03:00.0/host2/target2:0:0/2:0:0:0/block/sda/sda2
lrwxrwxrwx. 1 root root 0 Jan 18 06:51 sdb -> ../../devices/pci0000:00/0000:00:15.1/0000:04:00.0/host3/target3:0:0/3:0:0:0/block/sdb
lrwxrwxrwx. 1 root root 0 Jan 18 06:51 sdc -> ../../devices/pci0000:00/0000:00:18.0/0000:1b:00.0/host4/target4:0:0/4:0:0:0/block/sdc

# ls -l /sys/class/net | grep -w eth.
lrwxrwxrwx. 1 root root 0 Jan 18 06:51 eth0 -> ../../devices/pci0000:00/0000:00:16.0/0000:0b:00.0/net/eth0
lrwxrwxrwx. 1 root root 0 Jan 18 06:51 eth1 -> ../../devices/pci0000:00/0000:00:16.1/0000:0c:00.0/net/eth1
lrwxrwxrwx. 1 root root 0 Jan 18 06:51 eth2 -> ../../devices/pci0000:00/0000:00:17.0/0000:13:00.0/net/eth2
lrwxrwxrwx. 1 root root 0 Jan 18 06:51 eth3 -> ../../devices/pci0000:00/0000:00:17.1/0000:14:00.0/net/eth3

気付き

  • pciSlotNumber = "160" は図でみると、pciBridge4以降で接続されるデバイスの中で最初のデバイスである。よって、pciSlotNumber = "160" のデバイスは他のデバイスの追加・削除の影響を受ける事無く常に最初に認識されるデバイスと言える(VMwareの仕様なのか、図から明らかにSCSIコントローラやネットワークアダプタはpciBridge4〜pciBridge7に接続する構成である)。
    • SCSIコントローラ0が pciSlotNumber = "160" であれば、SCSIコントローラのなかでは最初に認識されることが保証されることになる。
    • (他に実施した検証(別途記事にする)の結果の先出しであるが)SCSIコントローラ0を pciSlotNumber = "160" にするという特別な優遇などはなにもない。SCSIコントローラ0が pciSlotNumber = "160" なのであれば、それは最初に追加した SCSIコントローラであったから、かつ、その後削除・再作成をしていないから、ということでしかない。
  • ネットワークアダプタについてはOS側で固定できるのでわざわざ難しく考えず、udev に頼れば良い。