服务器

版本:22.03 LTS SP4

FUSE fastpath特性说明和使用指南

介绍

FUSE(Filesystem in Userspace)允许非 root 用户在用户空间创建自己的文件系统,开发者不必修改内核代码,也无需内核模块开发经验,就能够快速实现新的、自定义的文件系统。FUSE 具有简化文件系统开发、扩展文件系统能力、提升安全性和稳定性的优点,但由于对 IO 性能的影响较大,其使用有所受限。为了提高 FUSE 的性能,openEuler 为 FUSE 提供了 fastpath 特性,该特性通过预创建线程绑核、共享内存、快速进程切换等功能实现了单线程和多线程下 FUSE 的加速。具体而言,有如下 3 项技术:

  1. 当挂载用户态文件系统时,就在每个 cpu 上都创建 1 个对应的 FUSE daemon,并将其绑定到对应的 CPU 上。当用户进程下发 IO 后,直接唤起同一 CPU 上的 FUSE daemon,这减少了创建和销毁线程的开销,同时可以尽可能利用 CPU 的 cache (用户进程和 FUSE daemon 通常会访问同样的内存)。每个线程绑核后相当于有了一个 percpu 的变量,不同 CPU 上的用户进程下发 IO 后不会互相竞争锁,提高了并行性。
  2. Fuse内核模块与FUSE daemon间创建一块共享内存,将FUSE的头信息直接放入到共享内存中,避免了寻址和复制部分的开销。
  3. 通过快速线程切换技术,可以在内核中显式地切换 CPU 上运行的线程,无需常规的线程切换流程,实现更高的 CPU 利用率。

使用方式

环境要求

  • 硬件要求:ARM64架构处理器。
  • 软件要求:目前 FUSE fastpath 特性仅在 openEuler 22.03 LTS SP4 上提供,且需要内核态和用户态的配合。使能 fastpath 特性要求内核最低版本为 5.10.0-264.0.0;用户态部分目前仅对 libfuse 进行了适配,要求 fuse3 软件包的最低版本为 3.10.5-11。开发者可以在 openEuler 的 update 源中获取到更新的软件包。

使用方法

动态库链接

fastpath 特性用户态部分以 fuse3 软件包中的 libfuse 动态库形式提供。对于使用 libfuse 动态库的用户态文件系统,链接时可以使用如下方式:

bash
gcc program.o -L/path/to/lib -lfuse -o program

对于已经编译为二进制的用户态文件系统,开发者可以通过如下命令查看当前链接的动态库:

bash
ldd /path/to/binary

若当前链接的动态库并非目标版本,可以使用如下方法:

  • 方法一:更新 /etc/ld.so.conf

    打开或创建一个新的配置文件,例如 /etc/d.so.conf.d/newlib.conf,并添加库所在路径:

    bash
    /path/to/lib

    运行以下命令更新缓存:

    bash
    sudo ldconfig
  • 方法二:设置 LD_LIBRARY_PATH

    可以通过设置环境变量 LD_LIBRARY_PATH 来指定库的路径。这种方法不需要管理员权限,但只对当前会话有效。

    bash
    export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH

适配和使能

确定链接到正确版本的动态库后,开发者可以通过在创建 fuse session 时增加参数的方式使能 fastpath,具体而言,包括以下3个配置:

  • use_fastpath:使能 fastpath,即上述的预创建线程绑核、共享内存、快速进程切换功能。
  • no_interrupt:不处理 interrupt 请求。
  • no_forget:不处理 forget 请求。

no_forget 和 no_interrupt 可以提升性能,但会造成部分功能缺失,需确定用户态文件系统不会使用这两类请求时再启用,否则可能造成问题。

需要注意的是,增加参数是指在使用 libfuse 中创建 fuse session 的函数 fuse_session_new 时传入的参数,而非使用挂载所需用户态文件系统的二进制时增加的参数,具体的适配方式因具体的用户态文件系统而异。以libfuse自带的演示demo ( passthrough_hp ) 为例,其代码需做如下适配:

diff
diff --git a/example/passthrough_hp.cc b/example/passthrough_hp.cc
index 872fc73..1f96820 100644
--- a/example/passthrough_hp.cc
+++ b/example/passthrough_hp.cc
@@ -1146,7 +1146,10 @@ static cxxopts::ParseResult parse_options(int argc, char **argv) {
         ("help", "Print help")
         ("nocache", "Disable all caching")
         ("nosplice", "Do not use splice(2) to transfer data")
-        ("single", "Run single-threaded");
+        ("single", "Run single-threaded")
+        ("nointerrupt", "Do not process interrupt request")
+        ("noforget", "Do not process forget request")
+        ("usefastpath", "use fastpath");
 
     // FIXME: Find a better way to limit the try clause to just
     // opt_parser.parse() (cf. https://github.com/jarro2783/cxxopts/issues/146)
@@ -1225,7 +1228,10 @@ int main(int argc, char *argv[]) {
     if (fuse_opt_add_arg(&args, argv[0]) ||
         fuse_opt_add_arg(&args, "-o") ||
         fuse_opt_add_arg(&args, "default_permissions,fsname=hpps") ||
-        (options.count("debug-fuse") && fuse_opt_add_arg(&args, "-odebug")))
+        (options.count("debug-fuse") && fuse_opt_add_arg(&args, "-odebug")) ||
+        (options.count("nointerrupt") && fuse_opt_add_arg(&args, "-ono_interrupt")) ||
+        (options.count("noforget") && fuse_opt_add_arg(&args, "-ono_forget")) ||
+        (options.count("usefastpath") && fuse_opt_add_arg(&args, "-ouse_fastpath")))
         errx(3, "ERROR: Out of memory");
 
     fuse_lowlevel_ops sfs_oper {};

挂载方式:

bash
passthrough_hp --usefastpath /path/to/src /path/to/mnt

参数 --usefastpath 进入 passthrough_hp 后会被解析为 -ouse_fastpath 参数,用于创建新的 fuse session,带有该参数的 session 后续在挂载和创建 FUSE daemon 时会使能fastpath。