1. 程式人生 > >mount過程分析之五(mount_bdev->fill_super)

mount過程分析之五(mount_bdev->fill_super)

sys_mount - > do_mount -> do_new_mount -> vfs_kern_mount -> mount_fs -> xfs_fs_mount -> mount_bdev

mount_bdev是針對塊裝置掛載時使用的函式,此外還有mount_nodev, mount_single等函式,分別用於不同的掛載情況,這裡以mount_bdev為例繼續講解。看一下mount_bdev的定義(fs/super.c中):
struct dentry *mount_bdev(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *data,
        int (*fill_super)(struct super_block *, void *, int))
{
        struct block_device *bdev;
        struct super_block *s;
        fmode_t mode = FMODE_READ | FMODE_EXCL;
        int error = 0;

        if (!(flags & MS_RDONLY))
                mode |= FMODE_WRITE;

        // 通過dev_name裝置名(如/dev/sda1)得到對應的block_device結構
        // 首先是一個路徑查詢的過程,呼叫kern_path()得到struct path
        // 然後以path.dentry->d_inode為引數呼叫bd_acquire得到block_device結構
        // 對於路徑查詢和塊裝置的問題以後再敘述
        bdev = blkdev_get_by_path(dev_name, mode, fs_type);
        if (IS_ERR(bdev))
                return ERR_CAST(bdev);

        /*
         * once the super is inserted into the list by sget, s_umount
         * will protect the lockfs code from trying to start a snapshot
         * while we are mounting
         */
        mutex_lock(&bdev->bd_fsfreeze_mutex);
        if (bdev->bd_fsfreeze_count > 0) {
                mutex_unlock(&bdev->bd_fsfreeze_mutex);
                error = -EBUSY;
                goto error_bdev;
        }
        // sget現在現存fs_type->fs_supers連結串列中查詢已經存在的對應的超級塊例項(因為一個裝置可能已經被掛載過了),fs_supers是file_system_type的成員,它指向一個特定檔案系統下的所有超級塊例項的連結串列表頭。比較的過程就是遍歷fs_supers連結串列,用每一個super_block->s_bdev和sget的bdev引數做比較,比較他們是不是同一個裝置,test_bdev_super就是為了比較bdev而傳入的函式引數。
        // 如果沒能找到已經存在的超級塊例項,那就只能建立一個新的了。此時set_bdev_super函式就是用來把bdev引數設定到新建立的super_block的s_bdev域中。然後設定一下s_type和s_id(s_id這裡先初始化為檔案系統名,之後如果發現是磁碟裝置再改為磁碟裝置名),並把這個新的sb加入到全域性super_blocks連結串列,以及此file_system_type的fs_supers連結串列中。
        // 到此就得到了一個已知的或新的super_block例項,後面的工作都是為了填充這個super_block的內容,並把它加入到各種連結串列中。
        s = sget(fs_type, test_bdev_super, set_bdev_super, flags | MS_NOSEC,
                 bdev);
        mutex_unlock(&bdev->bd_fsfreeze_mutex);
        if (IS_ERR(s))
                goto error_s;

        // 這個if是判斷得到的sb是一個已經存在的還是一個新的sb,已經存在的sb的s_root已經被初始化了,新的sb的s_root還是空
        if (s->s_root) {
                // 處理已經存在的sb
                // 判斷此次的掛載flag是否和之前的掛載有讀/寫衝突,如果有衝突則返回錯誤
                if ((flags ^ s->s_flags) & MS_RDONLY) {
                        deactivate_locked_super(s);
                        error = -EBUSY;
                        goto error_bdev;
                }

                /*
                 * s_umount nests inside bd_mutex during
                 * __invalidate_device().  blkdev_put() acquires
                 * bd_mutex and can't be called under s_umount.  Drop
                 * s_umount temporarily.  This is safe as we're
                 * holding an active reference.
                 */
                up_write(&s->s_umount);
                // 因為已經有了之前存在的sb,也就是block_dev之前也分配過了,所以這個新的bdev就可以釋放了。這裡對應前面的blkdev_get_by_path
                blkdev_put(bdev, mode);
                down_write(&s->s_umount);
        } else {
                // 處理新的sb
                char b[BDEVNAME_SIZE];

                s->s_mode = mode;
                strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));
                sb_set_blocksize(s, block_size(bdev));
                // 設定了sb的mode, id, blocksize後,就到了fill_super的時候了。fill_super是一個函式引數,它由具體檔案系統自己實現,如xfs就實現了xfs_fs_fill_super。
                error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
                if (error) {
                        deactivate_locked_super(s);
                        goto error;
                }

                s->s_flags |= MS_ACTIVE;
                bdev->bd_super = s;
        }

        return dget(s->s_root);

error_s:
        error = PTR_ERR(s);
error_bdev:
        blkdev_put(bdev, mode);
error:
        return ERR_PTR(error);
}


mount_bdev函式的主要邏輯就是這樣的

blkdev_get_by_path根據裝置名得到block_device結構

sget得到已經存在或者新分配的super_block結構

如果是已經存在的sb,就釋放第一步得到的bdev結構

如果是新的sb,就呼叫檔案系統個別實現的fill_super函式繼續處理新的sb,並建立根inode, dentry

返回得到的s_root

可以看到fill_super函式將完成mount接下來重要的工作,我們來看一下xfs是如何做fill_super處理的。

sys_mount - > do_mount -> do_new_mount -> vfs_kern_mount -> mount_fs -> xfs_fs_mount -> mount_bdev -> xfs_fs_fill_super

xfs_fs_fill_super是一個和xfs具體實現相關的函式,要想完全瞭解裡面的實現需要對xfs進行細緻的研究。由於本章節旨在貫通mount的基本過程,對於具體檔案系統的特別實現並不做細緻說明,以免過多的分支內容擾亂主線內容的說明。關於xfs的具體實現會在以後做研究說明。

先來看一下xfs_fs_fill_super的程式碼(fs/xfs/xfs_super.c):
STATIC int
xfs_fs_fill_super(
        struct super_block      *sb,
        void                    *data,
        int                     silent)
{
        struct inode            *root;
        struct xfs_mount        *mp = NULL;
        int                     flags = 0, error = ENOMEM;

        // 雖然不想詳細說明xfs_mount結構,但是看到下面sb->s_fs_info = mp,s_fs_info是super_block結構中代表具體檔案系統實現的超級塊資訊的指標,所以這個xfs_mount儲存著xfs的超級塊資訊,這個資訊是xfs特有的。
        mp = kzalloc(sizeof(struct xfs_mount), GFP_KERNEL);
        if (!mp)
                goto out;

        spin_lock_init(&mp->m_sb_lock);
        mutex_init(&mp->m_growlock);
        atomic_set(&mp->m_active_trans, 0);
        INIT_DELAYED_WORK(&mp->m_reclaim_work, xfs_reclaim_worker);
        INIT_DELAYED_WORK(&mp->m_eofblocks_work, xfs_eofblocks_worker);

        mp->m_super = sb;
        sb->s_fs_info = mp;

        // 解析xfs特有的掛載選項,儲存在xfs_mount裡
        error = xfs_parseargs(mp, (char *)data);
        if (error)
                goto out_free_fsname;

        // 設定sb的最小blocksize
        sb_min_blocksize(sb, BBSIZE);
        // 設定sb的擴充套件屬性的處理函式
        sb->s_xattr = xfs_xattr_handlers;
        // 設定export操作的處理函式
        sb->s_export_op = &xfs_export_operations;
        // 設定quota的處理函式
#ifdef CONFIG_XFS_QUOTA
        sb->s_qcop = &xfs_quotactl_operations;
#endif
        // 設定super block的操作函式
        sb->s_op = &xfs_super_operations;

        if (silent)
                flags |= XFS_MFSI_QUIET;

        // 這裡是在處理指定了xfs的log或realtime volumes的情況,data volume之前已經處理了
        error = xfs_open_devices(mp);
        if (error)
                goto out_free_fsname;

        // 建立很多工作佇列,如log, data的工作佇列
        error = -xfs_init_mount_workqueues(mp);
        if (error)
                goto out_close_devices;

        // 初始化per_cpu的incore super block counters
        error = -xfs_icsb_init_counters(mp);
        if (error)
                goto out_destroy_workqueues;

        // 讀super block的資訊,主要儲存在mp->m_sb裡
        error = xfs_readsb(mp, flags);
        if (error)
                goto out_destroy_counters;

        // 根據上面讀出的super block的資訊,繼續賦值mp裡和mount引數相關的內容
        error = xfs_finish_flags(mp);
        if (error)
                goto out_free_sb;

        // 根據上面讀出的super block的資訊,設定xfs的buffer target結構
        // 首先是data device的,然後判斷是否有log或realtime volumes,如果有也初始化它們的buffer target
        error = xfs_setup_devices(mp);
        if (error)
                goto out_free_sb;

        // 建立MRU cache為這個mount結構,為檔案流操作使用
        error = xfs_filestream_mount(mp);
        if (error)
                goto out_free_sb;

        /*
         * we must configure the block size in the superblock before we run the
         * full mount process as the mount process can lookup and cache inodes.
         */
         // 根據上面讀出的super block的內容繼續進行設定
        sb->s_magic = XFS_SB_MAGIC;
        sb->s_blocksize = mp->m_sb.sb_blocksize;
        sb->s_blocksize_bits = ffs(sb->s_blocksize) - 1;
        sb->s_maxbytes = xfs_max_file_offset(sb->s_blocksize_bits);
        sb->s_max_links = XFS_MAXLINK;
        sb->s_time_gran = 1;
        set_posix_acl_flag(sb);

        /* version 5 superblocks support inode version counters. */
        if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_5)
                sb->s_flags |= MS_I_VERSION;

        // 記憶體中的根inode在這裡通過xfs_iget得到
        error = xfs_mountfs(mp);
        if (error)
                goto out_filestream_unmount;

        root = igrab(VFS_I(mp->m_rootip));
        if (!root) {
                error = ENOENT;
                goto out_unmount;
        }
        // 根dentry在這裡建立
        sb->s_root = d_make_root(root);
        if (!sb->s_root) {
                error = ENOMEM;
                goto out_unmount;
        }

       // 至此struct mount初始化完畢
        return 0;

 out_filestream_unmount:
        xfs_filestream_unmount(mp);
 out_free_sb:
        xfs_freesb(mp);
 out_destroy_counters:
        xfs_icsb_destroy_counters(mp);
out_destroy_workqueues:
        xfs_destroy_mount_workqueues(mp);
 out_close_devices:
        xfs_close_devices(mp);
 out_free_fsname:
        xfs_free_fsname(mp);
        kfree(mp);
 out:
        return -error;

 out_unmount:
        xfs_filestream_unmount(mp);
        xfs_unmountfs(mp);
        goto out_free_sb;
}



xfs_fs_fill_super實際上已經涉及到很多xfs相關的具體知識,再往下還會有更具體的device、和記憶體等相關的知識。對於xfs檔案系統是如何實現的,那將是很大篇幅的內容,在以後分析xfs時再做分析,本文就不再往深入多做贅餘了,以免擾亂主線的邏輯。我覺得到此只要知道具體檔案系統的fill_super函式根據自身的特點設定到mount結構返回給上層呼叫就可以了。

返回後得到了mount結構我們就可以進行最後一步,把mount結構加入的全域性檔案系統樹中了。

http://blog.csdn.net/zr_lang/article/details/40325241 (mount 七)

http://blog.csdn.net/zr_lang/article/details/40343899 (mount 六)

http://blog.csdn.net/zr_lang/article/details/40115013 (mount 五)

http://blog.csdn.net/zr_lang/article/details/40080979 (mount 四)

http://blog.csdn.net/zr_lang/article/details/40049305 (mount 三)

http://blog.csdn.net/zr_lang/article/details/40002285 (mount 二)

http://blog.csdn.net/zr_lang/article/details/39963253 (mount 一)