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ノードの検索で、リスト故、ループとしていますが、ループとなることは、少なくとも通常の環境では、まず無いと思います。





