読者です 読者をやめる 読者になる 読者になる

φ(・・*)ゞ ウーン mount namespaceめも

linux kernel

mount namespaceを作成するカーネル側の関数としてはcreate_mnt_ns()とcopy_mnt_ns()の2つがあるんだけどcreate_mnt_ns()のほうはbtrfsとnfs4しか使っていない模様。
これら2個のコールフローは以下のような流れ。 btrfsの場合はサブボリュームをマウントするときに使う。

mount_subvol() @fs/btrfs/super.c
  --> mount_subtree()
    --> create_mnt_ns()
      --> alloc_mnt_ns()

nfsの場合はsub treeのマウント時に使う。

nfs_follow_remote_path() @fs/nfs/nfs4super.c
  --> mount_subtree()
    --> create_mnt_ns()
      --> alloc_mnt_ns()

それ以外の場合はfork()系、unshare()、setns()の処理から下記のような流れで行われる。

fork()系。

copy_process()
  --> copy_namespaces()
    --> create_new_namespaces() @kernel/nsproxy.c
      --> copy_mnt_ns()
        --> alloc_mnt_ns()

unshare()。

unshare()
  --> unshare_nsproxy_namespaces()
    --> create_new_namespaces() @kernel/nsproxy.c
      --> copy_mnt_ns()
        --> alloc_mnt_ns()

setns()。

setns()
  --> create_new_namespaces() @kernel/nsproxy.c
    --> copy_mnt_ns()
      --> alloc_mnt_ns()

では、alloc_mnt_ns()から処理を見ていきましょう。

2467 /*
2468  * Assign a sequence number so we can detect when we attempt to bind
2469  * mount a reference to an older mount namespace into the current
2470  * mount namespace, preventing reference counting loops.  A 64bit
2471  * number incrementing at 10Ghz will take 12,427 years to wrap which
2472  * is effectively never, so we can ignore the possibility.
2473  */
2474 static atomic64_t mnt_ns_seq = ATOMIC64_INIT(1);
2475 
2476 static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns)
2477 {
2478         struct mnt_namespace *new_ns;
2479         int ret;
2480 
2481         new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
2482         if (!new_ns)
2483                 return ERR_PTR(-ENOMEM);
2484         ret = proc_alloc_inum(&new_ns->proc_inum);
2485         if (ret) {
2486                 kfree(new_ns);
2487                 return ERR_PTR(ret);
2488         }
2489         new_ns->seq = atomic64_add_return(1, &mnt_ns_seq);
2490         atomic_set(&new_ns->count, 1);
2491         new_ns->root = NULL;
2492         INIT_LIST_HEAD(&new_ns->list);
2493         init_waitqueue_head(&new_ns->poll);
2494         new_ns->event = 0;
2495         new_ns->user_ns = get_user_ns(user_ns);
2496         return new_ns;
2497 }

ここの処理内容で大切なのは↓ですかね。これは古いmount namespaceをカレントのnamespaceにbind mount使用とした時にmount関係がループしないようにするためっぽい。

2489         new_ns->seq = atomic64_add_return(1, &mnt_ns_seq);

copy_mnt_ns()はちょっと長め。
最初のチェックでCLONE_NEWNSフラグが立っていなければ参照カウンタを増やして終了。

2499 struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
2500                 struct user_namespace *user_ns, struct fs_struct *new_fs)
2501 {
2502         struct mnt_namespace *new_ns;
2503         struct vfsmount *rootmnt = NULL, *pwdmnt = NULL;
2504         struct mount *p, *q;
2505         struct mount *old;
2506         struct mount *new;
2507         int copy_flags;
2508 
2509         BUG_ON(!ns);
2510 
2511         if (likely(!(flags & CLONE_NEWNS))) {
2512                 get_mnt_ns(ns);
2513                 return ns;
2514         }
2515 

nsはfork()系の場合は今作っている子プロセスのstruct task_struct。

2516         old = ns->root;
2517 

前述したalloc_mnt_ns()でstruct mnt_namespaceのインスタンスを作成。

2518         new_ns = alloc_mnt_ns(user_ns);
2519         if (IS_ERR(new_ns))
2520                 return new_ns;
2521

copy_tree()に渡すフラグ作り。user namespaceの設定でちょっと変化がある。

2522         namespace_lock();
2523         /* First pass: copy the tree topology */
2524         copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE;
2525         if (user_ns != ns->user_ns)
2526                 copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED;

copy_tree()の詳細を調べる気はなかったのでざっくりと行くと、mountのツリー構造がコピーされる、

2527         new = copy_tree(old, old->mnt.mnt_root, copy_flags);
2528         if (IS_ERR(new)) {
2529                 namespace_unlock();
2530                 free_mnt_ns(new_ns);
2531                 return ERR_CAST(new);
2532         }

この時に実際にコピーをしているのはclone_mnt()だと思う。コピーの処理はcopy_flagsの内容によって変わる。例えばこことか。

880         if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE))
881                 mnt->mnt_group_id = 0; /* not a peer of original */
882         else
883                 mnt->mnt_group_id = old->mnt_group_id;

copy_tree()が終わって作成しているmnt_namespaceの/にコピーしたツリーが設定される。

2533         new_ns->root = new;
2534         list_add_tail(&new_ns->list, &new->mnt_list);
2535 

次のループはコメントにある通りだと思うので割愛。

2536         /*
2537          * Second pass: switch the tsk->fs->* elements and mark new vfsmounts
2538          * as belonging to new namespace.  We have already acquired a private
2539          * fs_struct, so tsk->fs->lock is not needed.
2540          */
2541         p = old;
2542         q = new;
2543         while (p) {
2544                 q->mnt_ns = new_ns;
2545                 if (new_fs) {
2546                         if (&p->mnt == new_fs->root.mnt) {
2547                                 new_fs->root.mnt = mntget(&q->mnt);
2548                                 rootmnt = &p->mnt;
2549                         }
2550                         if (&p->mnt == new_fs->pwd.mnt) {
2551                                 new_fs->pwd.mnt = mntget(&q->mnt);
2552                                 pwdmnt = &p->mnt;
2553                         }
2554                 }
2555                 p = next_mnt(p, old);
2556                 q = next_mnt(q, new);
2557                 if (!q)
2558                         break;
2559                 while (p->mnt.mnt_root != q->mnt.mnt_root)
2560                         p = next_mnt(p, old);
2561         }
2562         namespace_unlock();
2563 

最後に、必要ならmountしているファイルシステムの解放をする。

2564         if (rootmnt)
2565                 mntput(rootmnt);
2566         if (pwdmnt)
2567                 mntput(pwdmnt);
2568 
2569         return new_ns;
2570 }
2571 

プロのための Linuxシステム・10年効く技術 (Software Design plus)

プロのための Linuxシステム・10年効く技術 (Software Design plus)