umountのMNT_EXPIRE
Rev.3を表示中。最新版はこちら。
MNT_EXPIREのmanの説明です。マウントポイントに期限切れの印をつける。 マウントポイントが現在使用中でない場合、このフラグをつけて umount2() を初めて呼び出すと EAGAIN エラーで失敗するが、マウントポイントには期限切れ (expire) の印がつけられる。 そのマウントポイントはいずれかのプロセスがアクセスしない限り期限切れの印がついたままとなる。 もう一度 MNT_EXPIRE をつけて umount2() を呼び出すと、期限切れの印のついたマウントポイントが アンマウントされる。 このフラグを MNT_FORCE もしくはMNT_DETACH と同時に指定することはできない。
と言うことですが、要は、任意のファイルシステム(autoマウント群等)をumountするタイミング考慮した機能です。最初に、MNT_EXPIREでumountして、再度MNT_EXPIREでumountして、その間に、このファイルシステムをどこからも使用してなければumountできる。と言うものです。もし2回目のunmountでumountできなければ、そこから次のunmountまでの間で上記条件でunmount処理が行われます。
umount/umoun2は、do_umount()をumounはflag=0で、umoun2はflag=MNT_FORCE/MNT_DETACH/MNT_EXPIREを伴ってコールされます。
if (flags & MNT_EXPIRE)の時の処理で、umountするプロセスのrootのファイルシステムをunmountする場合、及びMNT_FORCE | MNT_DETACHがflagに設定されている場合エラーです。
if (mnt_get_count(mnt) != 2)はunmountするファイルシステムの参照カウンターのチェックです。alloc_vfsmnt()で新規にstruct mountを割り当てるとき、参照カウンターは1となります。do_umount()をコールで、unmount先パスを取得しています。従ってdo_umount()内では参照カウンターは2となり、そうでないなら他のプロセスでこのファイルシステムを参照しているということです。従って if (mnt_get_count(mnt) != 2) ならエラーです。
if (!xchg(&mnt->mnt_expiry_mark, 1))は、mnt->mnt_expiry_markに1をセットし、セット前のmnt->mnt_expiry_markを返します。もしmnt->mnt_expiry_mark=0なら、エラーとしてEAGAINを返します(処理としては正常に行ったということです)。もしmnt->mnt_expiry_mark=1なら、以降の処理へと進み、ここでは実際の通常のunmountの処理となります。
static int do_umount(struct mount *mnt, int flags)
{
struct super_block *sb = mnt->mnt.mnt_sb;
int retval;
LIST_HEAD(umount_list);
retval = security_sb_umount(&mnt->mnt, flags);
if (retval)
return retval;
:
if (flags & MNT_EXPIRE) {
if (&mnt->mnt == current->fs->root.mnt ||
flags & (MNT_FORCE | MNT_DETACH))
return -EINVAL;
br_write_lock(vfsmount_lock);
if (mnt_get_count(mnt) != 2) {
br_write_unlock(vfsmount_lock);
return -EBUSY;
}
br_write_unlock(vfsmount_lock);
if (!xchg(&mnt->mnt_expiry_mark, 1))
return -EAGAIN;
}
:
retval = -EBUSY;
if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) {
if (!list_empty(&mnt->mnt_list))
umount_tree(mnt, 1, &umount_list);
retval = 0;
}
br_write_unlock(vfsmount_lock);
up_write(&namespace_sem);
release_mounts(&umount_list);
return retval;
}
mntput()は、引数のファイルシステムを参照しなくなった時コールします。例えばパス検索等の処理で、まず該当するパスのファイルシステムの参照カウンタをインクリメントし、そのパスを参照しなくなったら、 mntput()をコールしmntput_no_expire()で参照カウンタをデクリメントします。この時、m->mnt_expiry_markが1なら、 m->mnt_expiry_mark = 0とし、MNT_EXPIREでのumountを無効にしています。
void mntput(struct vfsmount *mnt)
{
if (mnt) {
struct mount *m = real_mount(mnt);
/* avoid cacheline pingpong, hope gcc doesn't get "smart" */
if (unlikely(m->mnt_expiry_mark))
m->mnt_expiry_mark = 0;
mntput_no_expire(m);
}
}
ファイルシステムの参照カウンタは、CONFIG_SMPの時、CPU変数としていますが、 do_umount()でmnt_get_count()をコールする時点で、br_write_lock(vfsmount_lock)とロックしています。そうすると、わざわざCPU変数する意味がなくなってしまいます。バグでないけど、些細なことですが処理的に、よろしくないのではと。
unsigned int mnt_get_count(struct mount *mnt)
{
#ifdef CONFIG_SMP
unsigned int count = 0;
int cpu;
for_each_possible_cpu(cpu) {
count += per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_count;
}
return count;
#else
return mnt->mnt_count;
#endif
}




