XFSmkfs 命令属于仓库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 clicli变量首先会设置两个成员:

  • 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.logblockscfg.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->volnamelibxfs_device_open()打开设备后,会把fd记录下全局变量中。另外,libxfs_device_open()还会尝试设置设备的blocksize,通过ioctl的BLKBSZSET实现。设备打开后的fd会记录在a->dfd。最后,会调用platform_findsizes()函数来获得设备的block size(保存在a->dbsize)和size(设备大小,保存在a->dsize)。 注意,这里的dbsizedsize的大小是basic block,见下文介绍。

默认情况下,只有data设备,没有log和realtime设备,不需要在打开和获取响应的值。

初始化cache

初始化hash桶的数量为1K,保存在libxfs_bhash_size

然后调用cache_init初始化全局cache libxfs_bcache。这里需要注意一个重要的参数libxfs_bcache_operations,这个参数表示cache操作会调用的方法,包括磁盘读写操作。

struct cache_operations libxfs_bcache_operations = {
	.hash		= libxfs_bhash,
	.alloc		= libxfs_balloc,
	.flush		= libxfs_bflush,
	.relse		= libxfs_brelse,
	.compare	= libxfs_bcompare,
	.bulkrelse	= libxfs_bulkrelse
};

manage_zones

初始化内存分配zone,每个zone对应一种用途,不过zone其实只是用来跟踪分配了多少该类型的内存而已。

Basic Block

插入介绍一下 Basic Block的概念。在XFS中,这个概念表示物理设备的一个块,代码中有下面这么一段:

/*
 Block I/O parameterization.	A basic block (BB) is the lowest size of
 filesystem allocation, and must equal 512.  Length units given to bio
 routines are in BB's.
 */
#define BBSHIFT		9
#define BBSIZE		(1<<BBSHIFT)
#define BBMASK		(BBSIZE-1)
#define BTOBB(bytes)	(((__u64)(bytes) + BBSIZE - 1) >> BBSHIFT)
#define BTOBBT(bytes)	((__u64)(bytes) >> BBSHIFT)
#define BBTOB(bbs)	((bbs) << BBSHIFT)

Basic Block简称为BB,在代码中看到变量、函数或者宏有 bb或者 BB缩写的,就是指这个。BB的大小固定为512B,XFS的很多地方在表示size或者length时,会有以BB为单位的,例如上面的 platform_findsizes()获得的dbsizedsize,就是以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设置agsizeagcount,所以会调用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中。


知识共享许可协议本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。