用户态调用F2FS的ioctl以启动FG_GC

本文详细介绍了如何在19年的研究论文中,通过ioctl系统调用在移动设备的F2FS文件系统中执行前台段清理操作,包括格式化分区、创建文件、查找ioctl函数参数并实际编写C代码执行的过程。

19年文章“Reinforcement Learning based Background Segment Cleaning for Log-structured File System on Mobile Devices”中提到可以用ioctl来调用前台段清理,所以记录一下怎么调用。

1.对分区/dev/sda4进行格式化,并挂载到/data上。

2.因为ioctl是对文件系统中的文件进行的I/O控制,所以需要在文件系统中创建一个文件。这里创建:

mkdir /data/test_qwj/190.txt

3.因为用户态调用ioctl的形式是:

   #include <sys/ioctl.h>

   int ioctl(int fd, unsigned long request, ...);

参考:https://man7.org/linux/man-pages/man2/ioctl.2.html
所以,我们需要去源码中查找函数的参数。
这是f2fs的ioctl函数:

long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
		return -EIO;
	if (!f2fs_is_checkpoint_ready(F2FS_I_SB(file_inode(filp))))
		return -ENOSPC;

	switch (cmd) {
	case F2FS_IOC_GETFLAGS:
		return f2fs_ioc_getflags(filp, arg);
	case F2FS_IOC_SETFLAGS:
		return f2fs_ioc_setflags(filp, arg);
	case F2FS_IOC_GETVERSION:
		return f2fs_ioc_getversion(filp, arg);
	case F2FS_IOC_START_ATOMIC_WRITE:
		return f2fs_ioc_start_atomic_write(filp);
	case F2FS_IOC_COMMIT_ATOMIC_WRITE:
		return f2fs_ioc_commit_atomic_write(filp);
	case F2FS_IOC_START_VOLATILE_WRITE:
		return f2fs_ioc_start_volatile_write(filp);
	case F2FS_IOC_RELEASE_VOLATILE_WRITE:
		return f2fs_ioc_release_volatile_write(filp);
	case F2FS_IOC_ABORT_VOLATILE_WRITE:
		return f2fs_ioc_abort_volatile_write(filp);
	case F2FS_IOC_SHUTDOWN:
		return f2fs_ioc_shutdown(filp, arg);
	case FITRIM:
		return f2fs_ioc_fitrim(filp, arg);
	case F2FS_IOC_SET_ENCRYPTION_POLICY:
		return f2fs_ioc_set_encryption_policy(filp, arg);
	case F2FS_IOC_GET_ENCRYPTION_POLICY:
		return f2fs_ioc_get_encryption_policy(filp, arg);
	case F2FS_IOC_GET_ENCRYPTION_PWSALT:
		return f2fs_ioc_get_encryption_pwsalt(filp, arg);
	case FS_IOC_GET_ENCRYPTION_POLICY_EX:
		return f2fs_ioc_get_encryption_policy_ex(filp, arg);
	case FS_IOC_ADD_ENCRYPTION_KEY:
		return f2fs_ioc_add_encryption_key(filp, arg);
	case FS_IOC_REMOVE_ENCRYPTION_KEY:
		return f2fs_ioc_remove_encryption_key(filp, arg);
	case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
		return f2fs_ioc_remove_encryption_key_all_users(filp, arg);
	case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
		return f2fs_ioc_get_encryption_key_status(filp, arg);
	case FS_IOC_GET_ENCRYPTION_NONCE:
		return f2fs_ioc_get_encryption_nonce(filp, arg);
	case F2FS_IOC_GARBAGE_COLLECT:
		return f2fs_ioc_gc(filp, arg);
	case F2FS_IOC_GARBAGE_COLLECT_RANGE:
		return f2fs_ioc_gc_range(filp, arg);
	case F2FS_IOC_WRITE_CHECKPOINT:
		return f2fs_ioc_write_checkpoint(filp, arg);
	case F2FS_IOC_DEFRAGMENT:
		return f2fs_ioc_defragment(filp, arg);
	case F2FS_IOC_MOVE_RANGE:
		return f2fs_ioc_move_range(filp, arg);
	case F2FS_IOC_FLUSH_DEVICE:
		return f2fs_ioc_flush_device(filp, arg);
	case F2FS_IOC_GET_FEATURES:
		return f2fs_ioc_get_features(filp, arg);
	case F2FS_IOC_FSGETXATTR:
		return f2fs_ioc_fsgetxattr(filp, arg);
	case F2FS_IOC_FSSETXATTR:
		return f2fs_ioc_fssetxattr(filp, arg);
	case F2FS_IOC_GET_PIN_FILE:
		return f2fs_ioc_get_pin_file(filp, arg);
	case F2FS_IOC_SET_PIN_FILE:
		return f2fs_ioc_set_pin_file(filp, arg);
	case F2FS_IOC_PRECACHE_EXTENTS:
		return f2fs_ioc_precache_extents(filp, arg);
	case F2FS_IOC_RESIZE_FS:
		return f2fs_ioc_resize_fs(filp, arg);
	case FS_IOC_ENABLE_VERITY:
		return f2fs_ioc_enable_verity(filp, arg);
	case FS_IOC_MEASURE_VERITY:
		return f2fs_ioc_measure_verity(filp, arg);
	case F2FS_IOC_GET_VOLUME_NAME:
		return f2fs_get_volume_name(filp, arg);
	case F2FS_IOC_SET_VOLUME_NAME:
		return f2fs_set_volume_name(filp, arg);
	case F2FS_IOC_GET_COMPRESS_BLOCKS:
		return f2fs_get_compress_blocks(filp, arg);
	case F2FS_IOC_RELEASE_COMPRESS_BLOCKS:
		return f2fs_release_compress_blocks(filp, arg);
	case F2FS_IOC_RESERVE_COMPRESS_BLOCKS:
		return f2fs_reserve_compress_blocks(filp, arg);
	default:
		return -ENOTTY;
	}
}

其中case F2FS_IOC_GARBAGE_COLLECT就是我们要找的上面函数参数的request,但是这是文件系统源码中的一个宏,我们不能直接使用,于是继续找F2FS_IOC_GARBAGE_COLLECT的定义:

#define F2FS_IOCTL_MAGIC		0xf5
#define F2FS_IOC_GARBAGE_COLLECT	_IOW(F2FS_IOCTL_MAGIC, 6, __u32)

所以我们得到我们要的参数request就是

_IOW(0xf5, 6, unsigned int)

又因为需要加一个数据(ioctl的第三个参数)用来表示调用的是前台GC,所以需要第三个参数1。

4.有了这些我们开始写一个.c文件来执行我们函数:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h> 


int main()
{

    int fd;
    int ret;
    unsigned int a=1;

    fd = open("/data/test_qwj/190.txt", O_RDWR);//其中/data为f2fs文件系统分区的挂载点
    if (fd < 0) {
        perror("open");
        exit(-2);
    }
    ret = ioctl(fd,_IOW(0xf5, 6, unsigned int),&a);//通过ioctl调用GC   
    if (ret) {
        perror("ioctl GC");
        exit(-4);
    }

    return 0;
}

其中要注意的是1要通过一个内存地址传进去。

5.编译执行即可

gcc test.c
./a.out
<think>嗯,用户这次问的是关于F2FS文件系统出现特定函数调用堆栈的原因,结合了提供的三个引用内容。用户可能正在调试内核问题,看到堆栈里出现f2fs_get_dnode_of_data、__f2fs_cluster_blocks这些函数名,想知道背后的触发场景。 从引用看,用户提供的资料都是关于F2FS源码分析的系列文章,特别是引用[1]明确列出了f2fs_map_block和get_dnode_of_data这些函数,引用[2]提到rename流程,引用[3]则涉及物理地址更新机制。用户应该已经看过这些材料但仍有疑问,可能需要更落地的故障场景解释。 用户应该是开发者或文件系统研究者,因为问题直接指向内核函数调用堆栈。ta的深层需求可能是:通过堆栈反推故障类型,或是理解F2FS内部执行路径。情绪可能有些困惑,毕竟内核堆栈对新手不太友好。 需要重点关联三个机制:dnode操作(涉及数据块寻址)、簇块管理(和空间分配相关)、以及引用[3]提到的Checkpoint更新流程。特别要注意__f2fs_cluster_blocks这个函数,它在垃圾回收和空间分配中都很关键。 可能的原因其实有层次:从常规操作(如访问文件)到异常处理(如磁盘空间不足)。引用[1]里文件数据读写和垃圾回收两节应该能解释大部分正常场景,但如果是错误堆栈,就要结合引用[3]的Checkpoint机制——比如同步元数据时遇到问题。 最后考虑用户可能需要后续排查方向,所以列出5个常见触发点,并把垃圾回收放在第一位(因为__f2fs_cluster_blocks最常见于GC)。还暗示了与碎片整理的关联,这是引用里没提但实际可能发生的场景。 </think>出现包含 `f2fs_get_dnode_of_data` 和 `__f2fs_cluster_blocks` 等函数调用的堆栈打印,通常与 **F2FS文件系统处理文件数据或元数据的操作**相关,具体可能由以下原因触发: --- ### **常见原因分析** 1. **文件读写操作**: - **`f2fs_get_dnode_of_data`**: - 核心作用是 **查找或分配文件数据块对应的物理地址**。 - 当应用程序执行文件读写(`read()`/`write()`)时,F2FS需将文件逻辑地址映射到物理地址,此过程会调用该函数[^1][^2]。 - 示例:用户程序读取大文件或随机写入时,堆栈可能频繁出现此函数。 2. **空间分配与回收**: - **`__f2fs_cluster_blocks`**: - 用于 **检查或分配连续的数据块簇(Cluster)**。 - 触发场景: - **文件写入**:分配新数据块时尝试合并连续块。 - **垃圾回收(GC)**:GC线程移动数据时需检查块的连续性[^1][^3]。 - **碎片整理**:重组文件数据以提升连续性。 3. **元数据更新操作**: - 文件创建、删除、重命名(`rename`)或截断(`truncate`)时,需更新文件的 `dnode`(数据节点索引结构),此时会调用 `f2fs_get_dnode_of_data` 定位索引节点[^2]。 - 若操作涉及大量元数据(如删除大目录),堆栈可能显示相关函数。 4. **异常处理与恢复**: - **磁盘空间不足**:当分配新块失败时,F2FS可能尝试回收空间(触发GC),堆栈中可能出现 `__f2fs_cluster_blocks`。 - **数据一致性保护**:Checkpoint过程(如 `f2fs_flush_nat_entries`)可能间接调用这些函数更新物理地址[^3]。 5. **文件系统检查(fsck)**: - 修复损坏的元数据或数据块时,会遍历 `dnode` 结构并验证块连续性,导致相关函数被调用。 --- ### **排查建议** 1. **结合日志上下文**: - 检查堆栈前后的函数(如 `f2fs_map_blocks`、`f2fs_vm_page_mkwrite`),确认是正常I/O还是GC/恢复流程。 - 观察是否伴随错误码(如 `-ENOSPC` 表示空间不足)。 2. **分析触发场景**: - 用户操作:是否正在读写大文件、执行 `rm -rf` 或 `mv`? - 系统行为:是否在低磁盘空间时触发GC? - 内核事件:是否在fsck或崩溃恢复后出现? 3. **检查硬件与配置**: - **SSD性能问题**:慢速SSD可能导致I/O堆积,使函数调用滞留在堆栈。 - **F2FS参数**:`gc_urgent` 模式可能增加GC频率。 --- ### **典型场景示例** ```plaintext # 文件写入时的堆栈片段 f2fs_file_write_iter # 用户写文件入口 f2fs_preallocate_blocks # 预分配空间 f2fs_get_dnode_of_data # 定位数据索引节点 f2fs_map_blocks # 映射逻辑块到物理地址 __f2fs_cluster_blocks # 检查连续块可用性 ``` --- ### **相关问题** 1. `f2fs_get_dnode_of_data` 失败可能引起哪些错误? 2. F2FS垃圾回收机制如何优化写放大问题?[^1][^3] 3. 如何诊断F2FS因磁盘空间不足导致的性能下降? 4. `f2fs_map_blocks` 与 `__f2fs_cluster_blocks` 在数据分配中的协作流程? 5. F2FS的Checkpoint机制如何保证崩溃一致性?[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值