XFS mkfs解析(1) - 参数的生成和校验
XFS的 mkfs 命令属于仓库https://git.kernel.org/pub/scm/fs/xfs/xfsprogs-dev.git,入口代码在 mkfs/xfs_mkfs.c文件的main
函数。
mkfs代码的第一部分主要用来生成和校验mkfs过程的参数,这些参数会保存在 struct mkfs_params cfg
中。
默认参数
变量struct mkfs_default_params dft
保存了mkfs的一些默认参数,其中有几个最常用的参数:
- sectorsize: 512B
- blocksize: 4096B
- crcs_enabled: true
还有一个变量struct libxfs_xinit xi
保存了libxfs初始化的配置。
CLI参数
命令行传递进来的参数会存放在变量struct cli_params cli
。cli
变量首先会设置两个成员:
- xi: 指向上面的
xi
变量的指针 - loginternal: 表示是否使用独立的设备记录journal log,默认是在当前设备内部记录,所以这个默认值为1。
在最简单的情况下,mkfs命令只接收一个设备名称,这个设备名称会保存在xi.volname
。
Setup Proto
默认情况下是不指定proto文件的,所以默认的proto值是:d--755 0 0 $
。
validate_blocksize
validate_blocksize()
函数会校验blocksize,并且设置到cfg
中。默认情况下cfg.blocksize
的值是4096B(上面的默认参数),cfg.blocklog
的值是12(4096的log指数,也就是2的12次方)。
注:xfsprogs中都使用log来表示一个变量保存的是另一个值的log指数,后面不再赘述。
validate_sectorsize
默认情况下,这个函数会设置cfg.sectorsize
的值为512(上面的默认值),cfg.sectorlog
的值为9。
另外,这个函数还会调用get_topology()
函数来获取设备的一些信息:
- lsectorsize:logical sector size
- psectorsize:physical sector size
- sunit: 最小io size,一般是512B,单位是BB(见下文)的情况下,值为0。
- swidth:最优io size,可能为0,单位是BB。
get_topolocy()
通过调用 libblkid的接口来获取的这些信息,并且保存在一个struct fs_topology
变量中,并且会赋值给cfg.sectorsize
(一般使用psectorsize)。
validate_log_sectorsize
设置并验证log的sector size。默认情况下,cli->loginternal
为true,所以cfg.lsectorsize
的值等于cfg.sectorsize
,也就是512B,cfg.lsectorlog
为对应值9。
validate_sb_features
检查feature的设置是否相互矛盾,feature设置保存在cfg.sb_feat
中。
validate_dirblocksize
默认情况下根据 blocksize来设置 dirblocksize,当 blocksize小于4KB时, dirblocksize设置为4KB,否则设置为 blocksize的值。
validate_inodesize
默认情况下crc是开启的,所以cfg.inodesize
的值是512B。cfg.inopblock
表示一个block里可以存放多少个inode,等于cfg.blocksize / cfg->inodesize
。
calc_dev_size
计算data, log和realtime分区的block数量。默认情况下,这些都不从命令行指定,表示使用整个设备。所以cfg.dblocks
, cfg.logblocks
和cfg.rtblocks
的值都为0。
calc_stripe_factors
计算条带参数。一般是使用前面get_topolocy()
得到的数据,最后会赋值给cfg.dsunit
等。一般情况下,cfg.dsunit
的值为0。
open_devices
打开设备以便校验mkfs参数是否有效,默认情况下支持 discard操作。
libxfs_init
open_devices()
函数首先调用libxfs_init(libxfs_init_t *a)
函数来打开设备并做一些初始化工作。
初始化Radix Tree
调用radix_tree_init()
初始化一个全局的radix tree。
打开设备
正常情况下,在这里时,a->volname
不为空,是一个指向设备的路径。确定设备路径可以打开后,调用open(rawfile, O_RDONLY)
打开设备,得到fd
。打开后,设置a->volname = NULL
。
接下来执行a->ddev = libxfs_device_open()
来打开data分区的设备,这个函数返回设备号。一般情况下,data设备就是我们上面打开的a->volname
。libxfs_device_open()
打开设备后,会把fd
记录下全局变量中。另外,libxfs_device_open()
还会尝试设置设备的blocksize,通过ioctl的BLKBSZSET实现。设备打开后的fd
会记录在a->dfd
。最后,会调用platform_findsizes()
函数来获得设备的block size(保存在a->dbsize
)和size(设备大小,保存在a->dsize
)。 注意,这里的dbsize
和dsize
的大小是basic block,见下文介绍。
默认情况下,只有data设备,没有log和realtime设备,不需要在打开和获取响应的值。
初始化cache
初始化hash桶的数量为1K,保存在libxfs_bhash_size
。
然后调用cache_init
初始化全局cache libxfs_bcache
。这里需要注意一个重要的参数libxfs_bcache_operations
,这个参数表示cache操作会调用的方法,包括磁盘读写操作。
manage_zones
初始化内存分配zone,每个zone对应一种用途,不过zone其实只是用来跟踪分配了多少该类型的内存而已。
Basic Block
插入介绍一下 Basic Block的概念。在XFS中,这个概念表示物理设备的一个块,代码中有下面这么一段:
Basic Block简称为BB,在代码中看到变量、函数或者宏有 bb或者 BB缩写的,就是指这个。BB的大小固定为512B,XFS的很多地方在表示size或者length时,会有以BB为单位的,例如上面的 platform_findsizes()
获得的dbsize
和dsize
,就是以BB为单位。
Discard Blocks
open_devices()
执行完libxfs_init()
之后,如果允许执行discard操作(默认允许),就会执行discard操作。因为默认情况下只有data设备,所以只会对data设备执行discard操作。
validate_datadev
默认情况下,xi->dsize == 1
, 而cfg->dblocks == 0
,所以会执行cfg->dblocks = DTOBT(xi->dsize, cfg->blocklog)
,这个会将设备大小转换成文件系统block数量(每个block大小是4KB)存储在cfg->dblocks
里。
默认情况下不会有log和realtime device,如果有也执行同样的操作。
calculate_initial_ag_geometry
计算Allocation Group的分布。默认情况下,用户不会从CLI设置agsize
和agcount
,所以会调用calc_default_ag_geometry()
。这个计算是根据设备的大小来确定的:
size >= 4TB
:agsize为1TB。size < 4TB
: 整个设备划分为4个AG,每个AG大小相等。
计算后,cfg.agsize
保存一个AG的大小,单位是block,cfg.agcount
保存AG的数量。
calculate_imaxpct
计算用多少空间来保存inode信息,也是根据设备的大小来确定:
size < 1TB
:25%的空间1TB <= size < 50TB
:5%的空间- 更大的设备,使用1%的空间。
这个值保存在cfg.imaxpct
中。