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 一)