vfsmountのリスト管理の検証(その2)
マウントのリスト管理の(その1)では、vfsmount構造体のmnt_mounts/mnt_childを検証してみました。ここではmnt_hashについてです。
マウントされているディレクトリを操作する時、そのマウント先のvfsmountを取得するわけですが、この時mnt_hashリストが参照されます。mnt_hashはmant_hashをノードとするヘッドになり、mount_hashtable下PAGE_SIZEの領域を有していて、その中にlist_head構造体が格納されています。list_head構造体は2つのポインタを持つメンバーですから、ページサイズを4K,ポインタサイズを4バイトとすると、500個のキーに対応するヘッドを、格納することが可能ということでしょうか。ハッシュ値はマウント先のvfsmount構造体のアドレスとマウントディレクトリのdentryのアドレスから算出されます。
実は先の内容と今回の内容にあたって、この事ゆえに検証してみようと思いたちました。500個のハッシュ値を有していて、確かにキーの衝突の可能性はあるものの、マウントという性格上、数百もマウントするようなケースがあるのだろうか?と、なにか他に意図があるのではと。
で、思ったのは、mountのmountとしたケースです。後でmountしたファイルシステムも、指定したマウント先(大元のファイルシステム)dentryのファイルシステム下として、マウントされるものと。そうなれば、この2つハッシュキーは同じになり、それをヘッドとしてリストされる事になります。複数のファイルシステムがマウントされても、リストの最後が最終マウント先として取得することができます。
このような実装だと、後のファイルシステムをmountしたまま、先のファイルシステムをumountできる事になります。
試してみると、最初にマウントしたファイルシステムはumountできませんでした。そうなると、後のマウント先は、最初にmountしたファイルシステムにmountされるのではないかと推測できます。
先のソースとほぼ同じです。なおpath_lookup()では最後にマウントしたvfsmountが返ってきます。mnt_hashリストの走査において、ヘッドから行っていません。(実はヘッドからと思いましたが。)ヘッドとなるmount_hashtableはnamespace.cでstatic変数で定義されていて、他から参照できないようになっています。
なお、mnt_hashのお陰で、数百のオーダでmountしてるシステムでも、瞬時にマウント先を検索できるわけです。
mnt_hashノードの検索で、リスト故、ループとしていますが、ループとなることは、少なくとも通常の環境では、まず無いと思います。
マウントされているディレクトリを操作する時、そのマウント先のvfsmountを取得するわけですが、この時mnt_hashリストが参照されます。mnt_hashはmant_hashをノードとするヘッドになり、mount_hashtable下PAGE_SIZEの領域を有していて、その中にlist_head構造体が格納されています。list_head構造体は2つのポインタを持つメンバーですから、ページサイズを4K,ポインタサイズを4バイトとすると、500個のキーに対応するヘッドを、格納することが可能ということでしょうか。ハッシュ値はマウント先のvfsmount構造体のアドレスとマウントディレクトリのdentryのアドレスから算出されます。
struct list_head *head = mount_hashtable + hash(mnt, dentry);パス検索でマウントされているディレクトリに遭遇する(dentryn->d_mount != 0)と、その時のvfsmountとdentryで、瞬時にマウント先vfsmountが取得できるわけです。
実は先の内容と今回の内容にあたって、この事ゆえに検証してみようと思いたちました。500個のハッシュ値を有していて、確かにキーの衝突の可能性はあるものの、マウントという性格上、数百もマウントするようなケースがあるのだろうか?と、なにか他に意図があるのではと。
で、思ったのは、mountのmountとしたケースです。後でmountしたファイルシステムも、指定したマウント先(大元のファイルシステム)dentryのファイルシステム下として、マウントされるものと。そうなれば、この2つハッシュキーは同じになり、それをヘッドとしてリストされる事になります。複数のファイルシステムがマウントされても、リストの最後が最終マウント先として取得することができます。
このような実装だと、後のファイルシステムをmountしたまま、先のファイルシステムをumountできる事になります。
試してみると、最初にマウントしたファイルシステムはumountできませんでした。そうなると、後のマウント先は、最初にmountしたファイルシステムにmountされるのではないかと推測できます。
[root@localhost kitamura]# mount disk1 /mnt/mntpoint [root@localhost kitamura]# mount disk2 /mnt/mntpoint [root@localhost kitamura]# umount disk1 umount: cannot unmount /tmp/kitamura/disk1 -- /tmp/kitamura/disk2 is mounted over it on the same point一つだけのマウントの場合
[root@localhost kitamura]# mount disk1 /mnt/mntpoint [root@localhost kitamura]# insmod testmod.ko ; dmesg ; rmmod testmod.ko : [ 256.725177] ceeeba00:parent mnt point[/]:mnt point[mntpoint] [ 256.725366] cec20698:hash hea重ねてマウントした場合
[root@localhost kitamura]# mount disk2 /mnt/mntpoint [root@localhost kitamura]# insmod testmod.ko ; dmesg ; rmmod testmod.ko : [ 1157.846838] ccbeb600:parent mnt point[mntpoint]:mnt point[/] [ 1157.847170] cec20c68:hash head重ねてマウントした場合とそうでない場合、hash headが異なっています。そして重ねマウントの親ファイルシステムのマウント先はmntpoint、これは先にマウントしたファイルシステムと言う事です。そしてマウントポイントの/は、先にマウントしたファイルシステムの/にマウントしていると言う事です。
先のソースとほぼ同じです。なおpath_lookup()では最後にマウントしたvfsmountが返ってきます。mnt_hashリストの走査において、ヘッドから行っていません。(実はヘッドからと思いましたが。)ヘッドとなるmount_hashtableはnamespace.cでstatic変数で定義されていて、他から参照できないようになっています。
#include <linux/init.h> #include <linux/module.h> #include <linux/namei.h> #include <linux/kernel.h> #include <linux/dcache.h> #include <linux/mnt_namespace.h> #include <linux/mount.h> #include <linux/path.h> #include <linux/fcntl.h> #include <linux/list.h> #include <linux/fs.h> MODULE_LICENSE("GPL"); static int get_mntinfo(struct vfsmount *mnt); static int test_init(void) { struct nameidata nd; int err = path_lookup("/mnt/mntpoint", LOOKUP_FOLLOW, &nd); if (err) { printk("path_look err.\n"); return -1; } get_mntinfo(nd.path.mnt); path_put(&nd.path); return 0; } static int get_mntinfo(struct vfsmount *mnt) { struct vfsmount *mnt_hash; struct list_head *p, *head; int cnt = 0; p = head = &mnt->mnt_hash; for (;;) { mnt_hash = list_entry(p, struct vfsmount, mnt_hash); if (p->next == head) { printk("%x:%s\n", p, "hash head"); break; } printk("%x:parent mnt point[%s]:mnt point[%s]\n", p, mnt_hash->mnt_parent->mnt_mountpoint->d_name.name, mnt_hash->mnt_mountpoint->d_name.name); p = p->next; } } static void test_exit(void) { } module_init(test_init); module_exit(test_exit);
補足
最後にpath_put(&nd.path)を呼び出しているのはumountする上で重要です。path_lookupで取得したstruct nameidata ndのvfsmountに使用中のフラグが設定され、そのファイルシステムがummountできなくなります。何回もrebootとする羽目になってしまいました。なお、mnt_hashのお陰で、数百のオーダでmountしてるシステムでも、瞬時にマウント先を検索できるわけです。
mnt_hashノードの検索で、リスト故、ループとしていますが、ループとなることは、少なくとも通常の環境では、まず無いと思います。