« stack storage » dans un environnement serveur


Je me propose de vous faire découvrir une « stack storage » typique que l’on retrouve coté serveur. Il y a des différences notables par rapport aux « stacks storage » coté poste client en raison de la complexité de certaines technologies inhérentes au monde serveur. A savoir : le multi-pathing, le fait que l’on va travailler sur des LUNs (du FC, du iSCSI, du SCSI locale) etc… L’idée étant de comprendre le cheminement que va prendre une IO pour atteindre un disque. i.e. quels devices\drivers vont prendre part à la résolution d’une IO.

 Attention, ça pique un peu. Bonne lecture !

 

Le dump que nous allons analyser a été généré sur un Windows 2016 (TP3) avec une topologie représentative (mais non exhaustive) de ce que l’on peut rencontrer.

1: kd> vertarget

Windows 10 Kernel Version 10514 MP (4 procs) Free x64

Product: Server, suite: TerminalServer DataCenter SingleUserTS

Built by: 10514.0.amd64fre.th2_release.150808-1529

Machine Name: "NUC1"

Kernel base = 0xfffff801`31873000 PsLoadedModuleList = 0xfffff801`31b9fb10

Debug session time: Wed Sep 30 00:04:08.423 2015 (UTC + 1:00)

System Uptime: 0 days 0:11:04.218

Pour connaitre le nombre de volumes que Windows « voit », il faut simplement rechercher les DEVICE_OBJECT gérés par le driver volmgr.sys. Dans notre cas, nous avons 4 devices:

 

1: kd> !drvobj \driver\volmgr

Driver object (ffffe000abe2cc00) is for:

\Driver\volmgr

Driver Extension List: (id , addr)

    Device Object list:

ffffe000adb4bac0  ffffe000ac832c90  ffffe000ac835980  ffffe000ac3d5580

 

 

Il faut savoir que pour le driver volmgr.sys (comme pour certains autres), il y a toujours un device servant à « gérer » les autres devices. i.e. il y a un device qui ne représente pas un volume réel. Concernant volmgr.sys, il s’appelle toujours VolMgrControl et, est le premier device créé (donc le dernier dans la liste ci-dessus):

1: kd> !devobj ffffe000ac3d5580

Device object (ffffe000ac3d5580) is for:

VolMgrControl \Driver\volmgr DriverObject ffffe000abe2cc00

Current Irp 00000000 RefCount 0 Type 00000012 Flags 00000840

Dacl ffffc101230c67f0 DevExt ffffe000ac3d56d0 DevObjExt ffffe000ac3d5860

ExtensionFlags (0x00000800)  DOE_DEFAULT_SD_PRESENT

Characteristics (0x00000100)  FILE_DEVICE_SECURE_OPEN

AttachedTo (Lower) ffffe000abfa2e40 \Driver\PnpManager

Device queue is not busy.

 

 

Nous savons donc que Windows, au moment de la génération du dump, « voyait » 3 volumes => HarddiskVolume 1 à 3. Les voici :

 

1: kd> !devobj ffffe000adb4bac0

Device object (ffffe000adb4bac0) is for:

HarddiskVolume3 \Driver\volmgr DriverObject ffffe000abe2cc00

Current Irp 00000000 RefCount 22 Type 00000007 Flags 00003050

Vpb ffffe000adb3efa0 Dacl ffffc101230c67f0 DevExt ffffe000adb4bc10 DevObjExt ffffe000adb4bdc0 Dope ffffe000adb39d10 DevNode ffffe000ad6c7360

ExtensionFlags (0x00000800)  DOE_DEFAULT_SD_PRESENT

Characteristics (0x00020000)  FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL

AttachedDevice (Upper) ffffe000adaf7040 \Driver\volsnap

Device queue is not busy.

1: kd> !devobj ffffe000ac832c90

Device object (ffffe000ac832c90) is for:

HarddiskVolume2 \Driver\volmgr DriverObject ffffe000abe2cc00

Current Irp 00000000 RefCount 1867 Type 00000007 Flags 00001150

Vpb ffffe000ac83a5c0 Dacl ffffc101230c67f0 DevExt ffffe000ac832de0 DevObjExt ffffe000ac832f90 Dope ffffe000ac83a550 DevNode ffffe000ac832960

ExtensionFlags (0x00000800)  DOE_DEFAULT_SD_PRESENT

Characteristics (0x00020000)  FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL

AttachedDevice (Upper) ffffe000ac831040 \Driver\volsnap

Device queue is not busy.

1: kd> !devobj ffffe000ac835980

Device object (ffffe000ac835980) is for:

HarddiskVolume1 \Driver\volmgr DriverObject ffffe000abe2cc00

Current Irp 00000000 RefCount 20 Type 00000007 Flags 00203050

Vpb ffffe000ac8358c0 Dacl ffffc101230c67f0 DevExt ffffe000ac835ad0 DevObjExt ffffe000ac835c80 Dope ffffe000abdf4cd0 DevNode ffffe000ac8355e0

ExtensionFlags (0x00000800)  DOE_DEFAULT_SD_PRESENT

Characteristics (0x00020000)  FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL

AttachedDevice (Upper) ffffe000ac833040 \Driver\volsnap

Device queue is not busy.

  

 

Sur ces 3 volumes, il y en 2 qui correspondent : au volume de 300MB pour la base BCD et au volume Système de Windows (typiquement, ce que nous retrouverons sur un poste client). Ce sont respectivement les 2 premiers devices créés durant la phase de boot. Nous pouvons d’ailleurs retrouver notre volume système facilement avec cette commande :

 

1: kd> !driveinfo c:

Drive c:, DriveObject ffffc000230ea8d0

    Directory Object: ffffc00022e1e630  Name: C:

       Target String is '\Device\HarddiskVolume2'

        Drive Letter Index is 3 (C:)

    Volume DevObj: ffffe000ac832c90

    Vpb: ffffe000ac83a5c0  DeviceObject: ffffe000abe18030

    FileSystem: \FileSystem\Ntfs

    Volume has 0x1703f4e (free) / 0x1bd32ff (total) clusters of size 0x1000

    94271.3 of 113971 MB free

Nous pouvons également retrouver le nom des volumes (s’il y en a un) avec la commande !vpb (Volume Parameter Block ).

Pour le HarddiskVolume1 (BCD), nous avons « System Reserved »:

 

1: kd> !vpb ffffe000ac8358c0

Vpb at 0xffffe000ac8358c0

Flags: 0x1 mounted

DeviceObject: 0xffffe000acfc2030

RealDevice:   0xffffe000ac835980

RefCount: 20

Volume Label:                System Reserved

 

 

Pour le HarddiskVolume2 (C:), nous n’avons pas de nom (car pas de label):

 

1: kd> !vpb ffffe000ac83a5c0 

Vpb at 0xffffe000ac83a5c0

Flags: 0x1 mounted

DeviceObject: 0xffffe000abe18030

RealDevice:   0xffffe000ac832c90

RefCount: 1867

Volume Label:

 

 

Pour le HarddiskVolume3 (une LUN), nous avons « LU-08 »:

 

1: kd> !vpb ffffe000adb3efa0

Vpb at 0xffffe000adb3efa0

Flags: 0x1 mounted

DeviceObject: 0xffffe000adb30030

RealDevice:   0xffffe000adb4bac0

RefCount: 22

Volume Label:      LU-08

  

 

Ce que nous avons découvert correspond pour le moment à ça :

L’idée de cet article consistant à montrer une stack storage « serveur », nous allons maintenant rentrer dans les détails de notre LUN (LU-08) qui se trouve être notre volume HarddiskVolume3. Une stack storage est, en réalité, composée de plusieurs sous stacks différentes. Dans cet exemple, et pour illustrer un cas complexe réel, nous rencontrerons une stack File System, une stack Volume, une stack disk (MPIO dans notre cas) et enfin les stacks Storport (pour la gestion de la partie SCSI). Pourquoi ? parce qu’une IO à destination de notre LUN va traverser un file system (NTFS\FAT et tous les File System minifilter drivers), puis un volume, puis va traverser un disque multi-path (MPIO) pour ensuite atteindre une LUN sous la forme d’une commande SCSI à destination du stockage.

 

Commençons par la stack file system. Le point d’entrée de la stack File System se trouve être le DeviceObject de notre commande !vpb (voir plus haut) :

 

1: kd> !devstack 0xffffe000adb30030

  !DevObj           !DrvObj            !DevExt           ObjectName

  ffffe000ad961780  \FileSystem\FltMgr ffffe000ad9618d0 

> ffffe000adb30030  \FileSystem\Ntfs   ffffe000adb30180 

Nous savons donc que la LUN a été formatée au format ntfs car il y a un device ntfs sur la stack. Il y a également moyen de récupérer tous les filter drivers file system à partir du device fltmgr (Filter Manager) mais ce sera l’objet d’un autre article. Une stack va se lire de haut en bas. i.e. pour notre stack file system, une IO va passer d’abord par fltmgr.sys (et tous les minifilter drivers file system) puis par ntfs.sys. Nous en sommes ici pour le moment :

 


 

Poursuivons avec la stack volume :

 

1: kd> !devstack 0xffffe000adb4bac0

  !DevObj           !DrvObj            !DevExt           ObjectName

  ffffe000adaf7040  \Driver\volsnap    ffffe000adaf7190 

> ffffe000adb4bac0  \Driver\volmgr     ffffe000adb4bc10  HarddiskVolume3

!DevNode ffffe000ad6c7360 :

  DeviceInst is "STORAGE\Volume\{82402ec9-6156-11e5-89cc-00fec8f70d10}#0000000000100000"

  ServiceName is "volsnap"

Une IO va donc d’abord passer par volsnap.sys. Volsnap gère les snapshots VSS. C’est au niveau de volsnap.sys que va se prendre la décision d’aller chercher des données plutôt dans la diffarea (si tel devait être le cas, en cas de snapshot), ou plutôt sur le volume réel. Je ferai un article sur le sujet plus tard. Si une IO doit continuer son chemin vers notre LUN, alors elle va passer par le driver volmgr.sys.

 

 

 

 

Nous allons maintenant traverser la stack disque. Comme le multi-pathing a été activé, notre disque sera un disque MPIO. Pour faire le lien entre le volume et le disque, il va falloir aller chercher l’information dans la structure DEVICE_OBJECT_EXTENSION de notre volume. Chaque DEVICE peut maintenir une structure (DEVICE_OBJECT_EXTENSION) pour stocker des informations qui lui sont propres. Les structures en question étant non-publiques, nous allons devoir retrouver l’information en tâtonnant. Il faut dans un premier  temps rechercher les adresses qui correspondent à la structure de type DEVICE_OBJECT_EXTENSION de notre device volume. Pour se faire, il suffit d’utiliser la commande !devobj sur notre device volume puis de dumper les adresses comprises entre le champ DevObjExt (en rose) et DevExt (en bleu). Nous avons donc :

 

1: kd> !devobj ffffe000adb4bac0

Device object (ffffe000adb4bac0) is for:

HarddiskVolume3 \Driver\volmgr DriverObject ffffe000abe2cc00

Current Irp 00000000 RefCount 22 Type 00000007 Flags 00003050

Vpb ffffe000adb3efa0 Dacl ffffc101230c67f0 DevExt ffffe000adb4bc10 DevObjExt ffffe000adb4bdc0 Dope ffffe000adb39d10 DevNode ffffe000ad6c7360

ExtensionFlags (0x00000800)  DOE_DEFAULT_SD_PRESENT

Characteristics (0x00020000)  FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL

AttachedDevice (Upper) ffffe000adaf7040 \Driver\volsnap

Device queue is not busy.

 

1: kd> dq ffffe000adb4bc10 ffffe000adb4bdc0

ffffe000`adb4bc10  ffffe000`adb4bac0 ffffe000`ac3d56d0

ffffe000`adb4bc20  00000000`00000001 00000000`00000000

ffffe000`adb4bc30  ffffe000`ac3d57a0 ffffe000`ac832e00

ffffe000`adb4bc40  00000001`0000264f 00000000`00000000

ffffe000`adb4bc50  ffffe000`ad9759d0 00000001`00000002

ffffe000`adb4bc60  ffffe000`adb4bc60 ffffe000`adb4bc60

ffffe000`adb4bc70  ffffe000`ac3fe348 ffffe000`abfa8d90

ffffe000`adb4bc80  00000000`00000000 00000000`00000100

ffffe000`adb4bc90  00000001`00060000 ffffe000`adb4bc98

ffffe000`adb4bca0  ffffe000`adb4bc98 ffffe000`adb4bca8

ffffe000`adb4bcb0  ffffe000`adb4bca8 00000000`00000000

ffffe000`adb4bcc0  00000100`00010101 00000000`00000003

ffffe000`adb4bcd0  00000000`0070006e ffffc000`23941ef0

ffffe000`adb4bce0  00000000`00e400e2 ffffc000`23d4a010

ffffe000`adb4bcf0  00000000`00e400e2 ffffc000`23d48c00

ffffe000`adb4bd00  00000000`00000000 ffffe000`adafed00

ffffe000`adb4bd10  00000000`00000001 fffff801`5e9b7098

ffffe000`adb4bd20  fffff801`5e9bd5b0 fffff801`5e9bb180

ffffe000`adb4bd30  00000000`00000000 00000000`00000000

ffffe000`adb4bd40  00000000`00000000 fffff801`5e9bd300

ffffe000`adb4bd50  fffff801`31bda0a0 00000000`00000001

ffffe000`adb4bd60  00000000`00000000 00000000`00000000

ffffe000`adb4bd70  ffffe000`adb54b60 ffffe000`adb70060

ffffe000`adb4bd80  ffffe000`adb35b10 00000001`00000001

ffffe000`adb4bd90  00000000`00100000 0000000f`ffd00000

ffffe000`adb4bda0  00000000`715eb38b 00000000`00100000

ffffe000`adb4bdb0  00000000`00000000 00000000`00000000

ffffe000`adb4bdc0  00000000`0000000d

 

Nous avons à l’offset 0x168 ce que nous recherchons (faites-moi confiance). Si d’aventure dans une version ultérieure de Windows cette structure devait changer, vous pouvez toujours essayer de retrouver notre device en tâtonnant avec une commande !pool pour retrouver un pool tag de type Devi.

Notre stack disque ressemble donc à ça :

 

1: kd> !devstack ffffe000`adb70060

  !DevObj           !DrvObj            !DevExt           ObjectName

  ffffe000adb35b10  \Driver\partmgr    ffffe000adb35c60

  ffffe000adb35060  \Driver\disk       ffffe000adb351b0  DR1

> ffffe000adb70060  \Driver\mpio       ffffe000adb701b0  MPIODisk0

!DevNode ffffe000adb3c850 :

  DeviceInst is "MPIO\Disk&Ven_QNAP&Prod_iSCSI_Storage&Rev_4.0_\1&7f6ac24&0&3630303134303541323742433033304446343633443432304344393737384436"

  ServiceName is "disk"

 

 

Ce qui nous donne le schéma suivant:

 


 

Pour continuer, il faut maintenant rechercher le nombre de paths, et les découvrir. Comme pour le device volume, la structure n’est pas publique mais peut être retrouvée en tâtonnant selon la même technique s’agissant de MPIO. Nous avons donc :

 

1: kd> !devobj ffffe000adb70060

Device object (ffffe000adb70060) is for:

MPIODisk0 \Driver\mpio DriverObject ffffe000ac3dbd50

Current Irp 00000000 RefCount 0 Type 00000007 Flags 00001050

Dacl ffffc10123202f40 DevExt ffffe000adb701b0 DevObjExt ffffe000adb709b0 DevNode ffffe000adb3c850

ExtensionFlags (0x00000800)  DOE_DEFAULT_SD_PRESENT

Characteristics (0x00000100)  FILE_DEVICE_SECURE_OPEN

AttachedDevice (Upper) ffffe000adb35060 \Driver\disk

Device queue is not busy.

 

1: kd> dq ffffe000adb701b0 ffffe000adb709b0

ffffe000`adb701b0  00000000`00000002 0000264f`00000000

ffffe000`adb701c0  00000000`00060001 ffffe000`adb701c8

ffffe000`adb701d0  ffffe000`adb701c8 ffffe000`adb70060

ffffe000`adb701e0  ffffe000`ac42d050 ffffe000`adb70060

ffffe000`adb701f0  ffffe000`ac3dbd50 ffffe000`ac42d050

ffffe000`adb70200  00000000`00010101 00000100`00000100

ffffe000`adb70210  00000000`00010000 0000264e`00000000

ffffe000`adb70220  00000000`00000000 00000001`00000001

ffffe000`adb70230  00000000`00000001 00000002`00000002

ffffe000`adb70240  00000002`00000003 00000000`77010001

ffffe000`adb70250  00000000`00000000 ffffe000`adb6fd80

ffffe000`adb70260  ffffe000`adb6fee0 00000000`00000000

ffffe000`adb70270  00000000`00000000 ffffe000`adb6fcd0

ffffe000`adb70280  ffffe000`adb42630 00000000`00000000

ffffe000`adb70290  00000000`00000000 00000000`00000000

ffffe000`adb70910  ffffffff`ffffffff 00000000`00000000

ffffe000`adb70920  00000000`00000000 00000000`00000000

ffffe000`adb70930  00000000`00000000 00000000`00000000

ffffe000`adb70940  00000000`00000000 00000000`00000001

ffffe000`adb70950  00000000`00000000 00000000`00000000

ffffe000`adb70960  00000000`00060001 ffffe000`adb70968

ffffe000`adb70970  ffffe000`adb70968 00000000`00000000

ffffe000`adb70980  00000000`00000000 00000000`00000000

ffffe000`adb70990  00000000`00000000 00000000`00000000

ffffe000`adb709a0  00000000`00000000 00000000`00000000

 

 

A l’offset 0x78, nous avons le nombre de paths (2 pour cette configuration). A l’offset 0xb8 et 0x90 nous avons les pointeurs vers la structure (non publique) qui décrit nos 2 paths :

 

1: kd> dq ffffe000`adb6fcd0

ffffe000`adb6fcd0  ffffe000`ac927050 00000000`77010000

ffffe000`adb6fce0  00000000`00000000 ffffe000`adb70060

ffffe000`adb6fcf0  ffffe000`adb6fa50 ffffe000`adb22390

ffffe000`adb6fd00  00000000`00000000 00000000`77010000

ffffe000`adb6fd10  ffffe000`adb6f638 ffffe000`ada28060

ffffe000`adb6fd20  ffffe000`adb0b040 fffff801`5f091200

ffffe000`adb6fd30  ffffe000`adb0b750 00040000`00000009

ffffe000`adb6fd40  00000001`ffffffff ffffe000`ad772b20

 

1: kd> dq ffffe000`adb42630

ffffe000`adb42630  ffffe000`ac927050 00000000`77010001

ffffe000`adb42640  00000000`00000000 ffffe000`adb70060

ffffe000`adb42650  ffffe000`adb22490 ffffe000`adb493a0

ffffe000`adb42660  00000000`00000001 00000000`77010001

ffffe000`adb42670  ffffe000`adb6f638 ffffe000`adb6d060

ffffe000`adb42680  ffffe000`adb409b0 fffff801`5f091200

ffffe000`adb42690  ffffe000`adb6f010 00040000`00000009

ffffe000`adb426a0  00000001`ffffffff ffffe000`ad751070

 

 

Nous retrouvons en rouge ci-dessus les 2 paths ID, ainsi que les devices mini-port storport qui correspondent au 2 LUNs qui ont été présentées à cette machine. Ce qui nous donne :

1: kd> !devstack ffffe000`ada28060

  !DevObj           !DrvObj            !DevExt           ObjectName

  ffffe000adb49b10  \Driver\partmgr    ffffe000adb49c60 

  ffffe000adb0b040  \Driver\disk       ffffe000adb0b190 

> ffffe000ada28060  \Driver\iScsiPrt   ffffe000ada281b0  0000003d

!DevNode ffffe000adb427b0 :

  DeviceInst is "SCSI\Disk&Ven_QNAP&Prod_iSCSI_Storage\1&1c121344&0&000000"

  ServiceName is "disk"

 

1: kd> !devstack ffffe000`adb6d060

  !DevObj           !DrvObj            !DevExt           ObjectName

  ffffe000adb3fb10  \Driver\partmgr    ffffe000adb3fc60 

  ffffe000adb409b0  \Driver\disk       ffffe000adb40b00 

> ffffe000adb6d060  \Driver\iScsiPrt   ffffe000adb6d1b0  0000003e

!DevNode ffffe000adb49010 :

  DeviceInst is "SCSI\Disk&Ven_QNAP&Prod_iSCSI_Storage\1&1c121344&0&000100"

  ServiceName is "disk"

Il s’agit ici de disques iSCSI vers une baie de disque QNAP. Le driver mini-port storport dans ce cas est celui de Microsoft (msiscsi.sys). S’il avait été question de LUNs  fiber channel, nous aurions eu des devices QLogic ou Emulex (i.e. du code non Microsoft).

 Ce qui nous donne le schéma suivant:

 

 

 

 

Pour résumer : 2 LUNs ont été présentées à Windows pour attaquer le même disque coté baie mais via 2 chemins différents (redondance). Ces 2 LUNs ont été « multipathées » coté Windows ce qui fait que le disque manager ne voit qu’un disque (le disque MPIO). Les IOs (si elles ne peuvent être résolues par le cache), vont traverser l’ensemble des devices\drivers depuis fltmgr.sys jusqu’au miniport storport pour transfert via la carte HBA ou adaptateur réseau (si iSCSI).

Si le multi-pathing est paramétré pour faire du round-robin (d’autres modes existent), alors les IOs seront distribuées successivement vers une LUN puis vers l’autres etc…

Merci de m’avoir lu.

Hervé Chapalain

Senior Escalation Engineer

Microsoft

Comments (1)

  1. JG Roche says:

    Un tres bon article didactique, Hervé
    On voit quand meme que tu "tâtonnes" facilement avec le code source 🙂

Skip to main content