如何让ceph-fuse单次下发更大IO请求

    最近项目里面要求写使用o_direct,但是测试时cephfs性能并不好。通过iostat查看底层磁盘利用率,实际上并不高。通过日志查看ceph-fuse的行为,发现其只进行128K的写。如何优化?简单但思路是将fuse发送给client的请求变得更大比如4M,这样每次IO传输更多的数据效率会更高。

    但无论将上层业务下发的块大小如何调整,这128K丝毫不会改变。通过ceph代码分析发现,其Client::ll_write并没有限制单次写的大小。调用ll_write来自fuse进程,是否是fuse系统出现的限制导致业务层的数据被限制切分下发到ceph-fuse实际执行的Client::ll_write里。

    笔者首先尝试fuse提供的原生能力,即big_write和max_write。虽然fuse提供了big_write功能,但在实际使用中并没有明显的效果。分析如下在开启O_DIRECT时,系统会调用fuse内核注册的fuse_direct_write_iter函数。该函数会在执行时对该inode进行加锁,以保证不会有并发写操作改文件。

    执行关键是fuse_direct_io函数,在fuse_iter_npages获得的是最大pages数这里的定义为32,一次最多读取4K*32即128K的数据(使用kmalloc分配)。

    当IO的一个块大小很大时,即count值可能很大,当然nmax代表的max_writes值也可能很大,取最小值。假如设置4M的块大小,max_writes为1M,那么按照1M从用户pages中获得数据。但是都会受限于req中最大的pages数量即32。

    big_write标志位的出现让能够让原先只填满一个page(4K)便返回的情况,变成填满多个page再返回,但pages多少受到FUSE_MAX_PAGES_PER_REQ限制。page选择两者中最小的值。max_write可变,但是FUSE_MAX_PAGES_PER_REQ的宏定义为32,也就是只要max_write超过128K(32*4K),数据都只能填满32个页,即128K。也就是内核中存在一个每次请求最多能处理的页数的限制,即每次/dev/fuse中读取拷贝内核空间的最大值。

    原生的big_write不能解决问题,笔者采用打算自行修改fuse相关的代码解决这一问题。使用fuse文件系统需要围绕着/dev/fuse这个内核设备做文章,这个设备类似于应用了生产者和消费者的模型。笔者发现需要修改除了以上fuse内核部分的限制之外libfuse中的还有一部分需要修改具体如下:       

    对于libfuse而言,其规定每个channel的最大缓冲区量,即每一次可以从该channel中获得多大的数据。代码位置在lib/fuse_kern_chan.c 中。原先设定为:

    #define MIN_BUFSIZE 0x20100

    ceph-fuse采用动态加载libfuse的方式,因此只要修改代码编译,并替换动态库即可。

    果然,在将每次请求量增大之后,性能有了非常明显的提升。发散一下:只要使用fuse的文件系统均会遇到同样的问题,可以作为参考。

你可能感兴趣的:(如何让ceph-fuse单次下发更大IO请求)