理想未来ってなんやねん

娘可愛い。お父さん頑張る。

tmpfs_unmount関数のソースコードを読む

tmpfs_mount関数に引き続き、tmpfs_unmount関数のコードを見ていきます。
tmpfs_unmount関数はfs/tmpfs/tmpfs_vfsops.cで定義されています。コードは下記の通りです。

static int
tmpfs_unmount(struct mount *mp, int mntflags)
{
        int error;
        int flags = 0;
        struct tmpfs_mount *tmp;
        struct tmpfs_node *node;

        /* Handle forced unmounts. */
        if (mntflags & MNT_FORCE)
                flags |= FORCECLOSE;

        /* Finalize all pending I/O. */
        error = vflush(mp, 0, flags, curthread);
        if (error != 0)
                return error;

        tmp = VFS_TO_TMPFS(mp);

        /* Free all associated data.  The loop iterates over the linked list
         * we have containing all used nodes.  For each of them that is
         * a directory, we free all its directory entries.  Note that after
         * freeing a node, it will automatically go to the available list,
         * so we will later have to iterate over it to release its items. */
        node = LIST_FIRST(&tmp->tm_nodes_used);
        while (node != NULL) {
                struct tmpfs_node *next;

                if (node->tn_type == VDIR) {
                        struct tmpfs_dirent *de;

                        de = TAILQ_FIRST(&node->tn_dir.tn_dirhead);
                        while (de != NULL) {
                                struct tmpfs_dirent *nde;

                                nde = TAILQ_NEXT(de, td_entries);
                                tmpfs_free_dirent(tmp, de, FALSE);
                                de = nde;
                                node->tn_size -= sizeof(struct tmpfs_dirent);
                        }
                }

                next = LIST_NEXT(node, tn_entries);
                tmpfs_free_node(tmp, node);
                node = next;
        }

        uma_zdestroy(tmp->tm_dirent_pool);
        uma_zdestroy(tmp->tm_node_pool);
        delete_unrhdr(tmp->tm_ino_unr);

        mtx_destroy(&tmp->allnode_lock);
        MPASS(tmp->tm_pages_used == 0);
        MPASS(tmp->tm_nodes_inuse == 0);

        /* Throw away the tmpfs_mount structure. */
        free(mp->mnt_data, M_TMPFSMNT);
        mp->mnt_data = NULL;

        MNT_ILOCK(mp);
        mp->mnt_flag &= ~MNT_LOCAL;
        MNT_IUNLOCK(mp);
        return 0;
}


tmpfs_umount関数の始めから順番に見ていきます。

static int
tmpfs_unmount(struct mount *mp, int mntflags)
{

tmpfs_umount関数はvfsops構造体変数tmpfs_vfsopsのメンバvfs_unmountにセットされており、kern/vfs_mount.cのdounmount関数からVFS_UNMOUNTマクロを通して呼ばれています。

        /* Handle forced unmounts. */
        if (mntflags & MNT_FORCE)
                flags |= FORCECLOSE;

強制アンマウントのフラグのチェック。
umountコマンドで-fオプションを指定した場合にフラグがセットされて呼ばれます。


        /* Finalize all pending I/O. */
        error = vflush(mp, 0, flags, curthread);
        if (error != 0)
                return error;

vflush関数によりmount 構造体mpに属する vnode テーブル中のすべてのvnode を削除しています。
flagsにFORCECLOSEがセットされている場合は、ビジー vnode を強制的にクローズします。

        tmp = VFS_TO_TMPFS(mp);

インライン関数VFS_TO_TMPFSによりmount構造体変数mpのメンバ変数mnt_dataからtmpfs_mount構造体のアドレスを取得しています。
VFS_TO_TMPFS関数はfs/tmpfs/tmpfs.hで次のように定義されています。

static inline
struct tmpfs_mount *
VFS_TO_TMPFS(struct mount *mp)
{
        struct tmpfs_mount *tmp;

        MPASS((mp) != NULL && (mp)->mnt_data != NULL);
        tmp = (struct tmpfs_mount *)(mp)->mnt_data;
        return tmp;
}


次は使用中のvnodeの開放処理です。

        /* Free all associated data.  The loop iterates over the linked list
         * we have containing all used nodes.  For each of them that is
         * a directory, we free all its directory entries.  Note that after
         * freeing a node, it will automatically go to the available list,
         * so we will later have to iterate over it to release its items. */
        node = LIST_FIRST(&tmp->tm_nodes_used);
        while (node != NULL) {
                struct tmpfs_node *next;

                if (node->tn_type == VDIR) {
                        struct tmpfs_dirent *de;

                        de = TAILQ_FIRST(&node->tn_dir.tn_dirhead);
                        while (de != NULL) {
                                struct tmpfs_dirent *nde;

                                nde = TAILQ_NEXT(de, td_entries);
                                tmpfs_free_dirent(tmp, de, FALSE);
                                de = nde;
                                node->tn_size -= sizeof(struct tmpfs_dirent);
                        }
                }

                next = LIST_NEXT(node, tn_entries);
                tmpfs_free_node(tmp, node);
                node = next;
        }

まず気がついたのが再帰的に行わずに直線的に処理されていること。
tm_nodes_usedにアロケート済みのノードのリストが集められているので直線的に処理しています。

マクロがいくつか目立ちますが、sys/queue.hに定義されたリスト及びテールキューのデータ構造簡単に扱う為のマクロです。
sys/queue.hでは、単方向リスト、単方向テールキュー、双方向リスト、双方向テールキューの為のマクロが用意されています。
tmpfsでは使用済みvnodeには双方向リストを、ディレクトリには双方向テールキューが使われています。
詳細についてはQUEUE(3)のマニュアルに記載されています。

tmpfs_free_dirent関数はディレクトリエントリの開放、tmpfs_free_node関数はノードの解放を行っています。


        uma_zdestroy(tmp->tm_dirent_pool);
        uma_zdestroy(tmp->tm_node_pool);

uma_zcreate関数で作成したゾーンの破壊を行っています。

        delete_unrhdr(tmp->tm_ino_unr);

new_unrhdr関数で初期化したカーネルユニット番号アロケータの破壊です。

        mtx_destroy(&tmp->allnode_lock);
        MPASS(tmp->tm_pages_used == 0);
        MPASS(tmp->tm_nodes_inuse == 0);

ノード監理用のミューテックスの破壊とMPASS(KASSERT)によるチェック。

        /* Throw away the tmpfs_mount structure. */
        free(mp->mnt_data, M_TMPFSMNT);
        mp->mnt_data = NULL;

        MNT_ILOCK(mp);
        mp->mnt_flag &= ~MNT_LOCAL;
        MNT_IUNLOCK(mp);
        return 0;

最後にtmpfs_mount構造体で使用していたメモリ領域の開放と、アンマウントによりローカルストレージではなくなったのでMNT_LOCALフラグのリセットしています。