Coder
分类: LINUX
2010-07-27 01:11:13
分配超级块对象
许多文件系统对象的get_sb方法都是由单行函数实现的。例如,在Ext3文件系统中该方法实现如下:
---------------------------------------------------------------------
fs/ext3/super.c
3009 static int ext3_get_sb(struct file_system_type *fs_type,
3010 int flags, const char *dev_name, void *data, struct vfsmount *mnt)
3011 {
3012 return get_sb_bdev(fs_type, flags, dev_name, data, ext3_fill_super, mnt);
3013 }
---------------------------------------------------------------------
get_sb_bdev() VFS函数分配并初始化一个新的适合于磁盘文件系统的超级块。它接收ext3_fill_super函数的地址作为参数,该函数从Ext3磁盘分区读取磁盘超级块。为了分配适合于特殊文件系统的超级块,VFS也提供get_sb_pseudo()函数(对于没有挂载点的特殊文件系统,例如pipefs 、sockfs、bdevfs等)、get_sb_single()函数(对于具有唯一挂载点的特殊文件系统,例如sysfs)、get_sb_nodev()函数(对于可以挂载多次的特殊文件系统,例如tmpfs、ramfs等)以及get_sb_ns()()函数。
get_sb_bdev()函数定义如下:
---------------------------------------------------------------------
fs/super.c
784 int get_sb_bdev(struct file_system_type *fs_type,
785 int flags, const char *dev_name, void *data,
786 int (*fill_super)(struct super_block *, void *, int),
787 struct vfsmount *mnt)
788 {
789 struct block_device *bdev;
790 struct super_block *s;
791 fmode_t mode = FMODE_READ;
792 int error = 0;
793
794 if (!(flags & MS_RDONLY))
795 mode |= FMODE_WRITE;
796
797 bdev = open_bdev_exclusive(dev_name, mode, fs_type);
798 if (IS_ERR(bdev))
799 return PTR_ERR(bdev);
800
801 /*
802 * once the super is inserted into the list by sget, s_umount
803 * will protect the lockfs code from trying to start a snapshot
804 * while we are mounting
805 */
806 mutex_lock(&bdev->bd_fsfreeze_mutex);
807 if (bdev->bd_fsfreeze_count > 0) {
808 mutex_unlock(&bdev->bd_fsfreeze_mutex);
809 error = -EBUSY;
810 goto error_bdev;
811 }
812 s = sget(fs_type, test_bdev_super, set_bdev_super, bdev);
813 mutex_unlock(&bdev->bd_fsfreeze_mutex);
814 if (IS_ERR(s))
815 goto error_s;
816
817 if (s->s_root) {
818 if ((flags ^ s->s_flags) & MS_RDONLY) {
819 deactivate_locked_super(s);
820 error = -EBUSY;
821 goto error_bdev;
822 }
823
824 close_bdev_exclusive(bdev, mode);
825 } else {
826 char b[BDEVNAME_SIZE];
827
828 s->s_flags = flags;
829 s->s_mode = mode;
830 strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));
831 sb_set_blocksize(s, block_size(bdev));
832 error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
833 if (error) {
834 deactivate_locked_super(s);
835 goto error;
836 }
837
838 s->s_flags |= MS_ACTIVE;
839 bdev->bd_super = s;
840 }
841
842 simple_set_mnt(mnt, s);
843 return 0;
844
845 error_s:
846 error = PTR_ERR(s);
847 error_bdev:
848 close_bdev_exclusive(bdev, mode);
849 error:
850 return error;
851 }
---------------------------------------------------------------------
get_sb_bdev()执行的主要操作如下:
1、调用open_bdev_exclusive()打开设备文件名为dev_name的块设备。
2、调用sget()搜索文件系统的超级块对象链表(type->fs_supers,file_system_type的fs_supers字段将同文件系统类型的不同实例的超级块对象连接起来),我们看到传递的参数为fs_type、test_bdev_super、set_bdev_super和bdev。如果找到一个与打开的块设备相匹配的超级块,则返回它的地址。也就是同一个设备无论在多少个挂载点挂载多少次,系统中对应于该设备,实际上都只有一个super_block对象,由此保证了数据的同步。否则,分配并初始化一个新的超级块对象,把它插入到以文件系统的fs_supers字段为链表头的双向链表中,并返回其地址。我们来看sget()的定义:
---------------------------------------------------------------------
fs/super.c
339 struct super_block *sget(struct file_system_type *type,
340 int (*test)(struct super_block *,void *),
341 int (*set)(struct super_block *,void *),
342 void *data)
343 {
344 struct super_block *s = NULL;
345 struct super_block *old;
346 int err;
347
348 retry:
349 spin_lock(&sb_lock);
350 if (test) {
351 list_for_each_entry(old, &type->fs_supers, s_instances) {
352 if (!test(old, data))
353 continue;
354 if (!grab_super(old))
355 goto retry;
356 if (s) {
357 up_write(&s->s_umount);
358 destroy_super(s);
359 }
360 return old;
361 }
362 }
363 if (!s) {
364 spin_unlock(&sb_lock);
365 s = alloc_super(type);
366 if (!s)
367 return ERR_PTR(-ENOMEM);
368 goto retry;
369 }
370
371 err = set(s, data);
372 if (err) {
373 spin_unlock(&sb_lock);
374 up_write(&s->s_umount);
375 destroy_super(s);
376 return ERR_PTR(err);
377 }
378 s->s_type = type;
379 strlcpy(s->s_id, type->name, sizeof(s->s_id));
380 list_add_tail(&s->s_list, &super_blocks);
381 list_add(&s->s_instances, &type->fs_supers);
382 spin_unlock(&sb_lock);
383 get_filesystem(type);
384 return s;
385 }
---------------------------------------------------------------------
sget()函数首先通过一个循环,使用传递进来的test方法,逐个地检查文件系统类型的fs_supers链表中的元素,查看test_bdev_super()的定义,即检查超级块的s_bdev字段值是否与打开的块设备相同,若是,则将相应的超级块返回。
在文件系统类型的fs_supers链表中没有找到超级块,则调用alloc_super(type)来分配并初始化超级块。
调用传递进来的set(s, data)方法,查看set_bdev_super()函数的定义,则主要是设置超级块的s_bdev字段指向块设备结构,设置超级块的s_dev,即设备号字段为块设备的设备号。
设置超级块的s_type指向文件系统类型对象,设置超级块的s->s_id值为文件系统类型名。将超级块对象添加进系统的超级块对象链表,将超级块对象添加进文件系统类型的fs_supers链表。增加文件系统类型的引用计数。
3、如果不是新的超级块(通过sget()函数返回的super_block对象的s_root字段,即s->s_root是否为空来判断的),则首先检查flags 和s->s_flags是否仅有一个设置了MS_RDONLY。若是,则调用close_bdev_exclusive(bdev, mode)关闭块设备并返回-EBUSY;否则,仅仅调用close_bdev_exclusive(bdev, mode)关闭块设备。
4、超级块是新创建的,把参数flags中的值拷贝到超级块的s_flags字段,把mode中的值拷贝到超级块的s_mode字段,并将s_id及s_blocksize字段设置为块设备的合适值(s_id为由block_device所属的gendisk的设备名及分区号组成的字符串,s_blocksize为bdev->bd_block_size)。调用特定文件系统的超级块填充函数(例子中是ext3_fill_super函数),这个函数访问磁盘上的超级块信息,并填充新超级块对象的其他字段。设置超级块标志字段s->s_flags的MS_ACTIVE,以表示超级快可用。同时设置bdev->bd_super为超级块的地址
5、调用simple_set_mnt()简单设置mnt参数,即将vfsmount对象的mnt_sb字段设置为获得的super_block,增加super_block字段s_root所指向的dentry的引用计数,并将s_root字段值赋给mnt的mnt_root字段。
在这里,我们看到,在fill_super()方法返回的时候,其超级块对象的s_root,也就是文件系统的根目录目录项已经为有效值了。
6、返回0