zswap4BSD 功能测试

yyi
yyi
2024-03-11 / 0 评论 / 36 阅读 / 正在检测是否收录...

zswap4BSD 功能测试

概述

在已经迁移 zswap 至 FreeBSD之后,设计一类测试方法,梯度的完成如下两个目标

  1. 测试zswap的基础功能,包括

    1. zswap_init

      • zpool_init
      • crypto_init
      • zbud_register
    2. zswap_frontswap_store
    3. zswap_frontswap_load
    4. zswap_exit
  2. 测试zswap的性能

    1. 测试单位时间内swap可以换出、换入的页面数量,对比zswap换入、换出的页面数量,计算其加速比
    2. 比较Linux启用zswap时的加速比,和FreeBSD启用zswap时的加速比

测试基础功能的同时也兼有测试驱动开发的目的,完成整个zswap的开发

前置内容

由于是交叉编译,时刻注意相关环境的问题。

编译命令:

export BASEDIR=$(pwd)
export MAKEOBJDIRPREFIX=$BASEDIR/obj
cd $BASEDIR/src
./tools/build/make.py --clean --cross-bindir=/usr/lib/llvm-14/bin/ TARGET=amd64 TARGET_ARCH=amd64 -n
./tools/build/make.py --clean --cross-bindir=/usr/lib/llvm-14/bin/ TARGET=amd64 TARGET_ARCH=amd64 kernel-toolchain -s -j32 -DWITH_DISK_IMAGE_TOOLS_BOOTSTRAP
./tools/build/make.py --clean --cross-bindir=/usr/lib/llvm-14/bin/ TARGET=amd64 TARGET_ARCH=amd64 KERNCONF=GENERIC NO_MODULES=yes buildkernel -s -j32

基础功能测试

基础功能测试采用syscall为入口,分别为init、store、load、exit提供根据调用参数而来的分入口,后续可能支持触发更多事件用于benchmark

注册系统调用

accord:AddingSyscalls - FreeBSD Wiki

Syscall在sys/kern/syscalls.master处注册,在这个位置我们定义了它在用户空间所能展现的样子,先以一个int参数为例

583 AUE_ZSWAP_INTERFACE STD {
        int zswap_interface(
                int cmd
        );
}

之后通过make,生成sys/kern和sys/sys中的代码

make sysent

在这遇到了问题,我目测后续如果坚持在Linux下交叉编译会遇到更多的问题,考虑部署一个基于VMWare的FreeBSD工作环境

img

在lib/libc/sys/Symbol.map中添加符号

img

在sys/compact/freebsd32/syscalls.master添加32位兼容

img

在我们的zswap.c中进行实现

img

编译内核

Chapter 9. Building and Installing a FreeBSD Kernel 好吧这个有点简略,不是我想要的

看眼现在的

img

调下性能配置,之前2C1G太磕碜了,先来16C 8G

make buildkernel KERNCONF=GENERIC -s -j32

img

我们定义的这个Identifier没有

看看别的标志符在哪定义的

img

看上去少了个audit的步骤,我还以为不关心安全可以不用管。。

Auditing

Security event auditing allows fine-grained logging of security-relevant events in the operating system, and is described by the CAPP common criteria protection profile. Detailed information on configuring audit can be found in the FreeBSD Handbook http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/audit.html, and an implementation paper can be found on the TrustedBSD website http://www.TrustedBSD.org/.

Most system calls are considered security-relevant, as they frequently involve access control checks, communication between processes, or system configuration, and therefore almost all system calls will be audited. You can find more detailed information on the AddingAuditEvents page. Please do not forget to add appropriate auditing to your new system call, be it a native call or a compat one!

Please e-mail the trustedbsd-audit mailing list, or RobertWatson, if you require help. The audit event identifier space is managed by the OpenBSM project, and you will need to request assignment of a new identifier if an existing one isn't a good match.

AddingAuditEvents - FreeBSD Wiki

由于我们是测试,直接define为AUE_NULL好了

img

测试

编译好之后替换

make install
reboot

img

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>

// 定义操作码
#define OP_INIT 0
#define OP_SWAP_STORE 1
#define OP_SWAP_LOAD 2
#define OP_EXIT 3

// 定义zswap_interface系统调用的编号,根据你的系统实际情况替换583
#define SYS_zswap_interface 583

int main() {
    int result;

    // 调用OP_INIT
    printf("Calling OP_INIT...\n");
    result = syscall(SYS_zswap_interface, OP_INIT);
    if (result == 0) {
        printf("OP_INIT executed successfully.\n");
    } else {
        perror("syscall OP_INIT failed");
    }

    // 调用OP_SWAP_STORE (需要更多逻辑来处理存储操作)
    // 注意: 由于OP_SWAP_STORE和OP_SWAP_LOAD没有实现具体逻辑,这里的调用仅作为示例
    printf("Calling OP_SWAP_STORE...\n");
    result = syscall(SYS_zswap_interface, OP_SWAP_STORE);
    if (result == 0) {
        printf("OP_SWAP_STORE executed successfully.\n");
    } else {
        perror("syscall OP_SWAP_STORE failed");
    }

    // 调用OP_SWAP_LOAD (需要更多逻辑来处理加载操作)
    printf("Calling OP_SWAP_LOAD...\n");
    result = syscall(SYS_zswap_interface, OP_SWAP_LOAD);
    if (result == 0) {
        printf("OP_SWAP_LOAD executed successfully.\n");
    } else {
        perror("syscall OP_SWAP_LOAD failed");
    }

    // 调用OP_EXIT
    printf("Calling OP_EXIT...\n");
    result = syscall(SYS_zswap_interface, OP_EXIT);
    if (result == 0) {
        printf("OP_EXIT executed successfully.\n");
    } else {
        perror("syscall OP_EXIT failed");
    }

    return 0;
}

最开始的panic,竟然只是我起了个字符串叫panic,我真的晕,因为这个调了好一会。

搞了个小脚本,commit完了就可以直接直接替换了,大概几十秒的量级

#!/bin/sh

cd /work/zswap4BSD
sudo git pull
sudo make buildkernel -j16 NO_CLEAN=YES && \
sudo make installkernel && \
sudo reboot

在这个位置卡住,目测是访问acomp_ctx出问题了

4afd641b080e5509b257b7d1b00e6e4

5ea58b87bbc273220ecc4edf5d231d4

访问了一个0x440,导致kernel里 page fault了,这鬼东西显然不可能是一个内核的的地址

image-20240308185059604

稍作改造,之前cpuhp相关的callback都是注册为0的,我们知道我们不会涉及到cpuhp,那么直接把唯一的node传进去初始化就好了,再来一次。

现在每次调用必FATAL。。。都得等Reboot

image-20240308185722092

这块是store的第一个核心点,是压缩,fanyi做的。感觉要变成我自己的了。

image-20240308190120374

这个地方其实应该不难的,我们初始化输入输出的scatterlist(其实接的是uio),我感觉是这块有点小问题

整理一下这块实际上发生了啥吧

// 入参:u8* dst (acomp_ctx->dstmem);
// 入参:vm_page* page;
// 入参:uio input, output
// 1 :

sg_init_table() // did nothing

sg_set_page(input, page, PAGE_SIZE, 0) {
    struct iovec iov[1];
    iov[0].iov_base = (void *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(page));
    iov[0].iov_len = len;
    uio->uio_iov = iov;
    uio->uio_iovcnt = 1;
    uio->uio_offset = 0;
    uio->uio_resid = len;
    uio->uio_segflg = UIO_SYSSPACE;
}

sg_init_one(output, dst, PAGE_SIZE * 2) {
    struct iovec iov[1];
    iov[0].iov_base = (void *)buf;
    iov[0].iov_len = buflen;
    uio->uio_iov = iov;
    uio->uio_iovcnt = 1;
    uio->uio_offset = 0;
    uio->uio_resid = buflen;
    uio->uio_segflg = UIO_SYSSPACE;
    // uio_rw暂时无法设置
    return;
}

acomp_request_set_params(struct acomp_req *req, struct uio *input,
    struct uio *output, unsigned int slen, unsigned int dlen)
{
    // 设置uio_rw
    input->uio_rw = UIO_READ;
    output->uio_rw = UIO_WRITE;
    // 设置cryptop参数
    struct cryptop *crp = kzalloc(sizeof(struct cryptop),
        GFP_KERNEL); // linuxkpi
    crypto_initreq(crp, req->sid);
    crp->crp_flags = CRYPTO_F_CBIFSYNC | CRYPTO_F_CBIMM; // 存疑
    crp->crp_callback = crypto_callback;
    crypto_use_uio(crp, input);        // 使用input输入
    crypto_use_output_uio(crp, output); // 使用output输出
    crp->crp_payload_start = 0;
    crp->crp_payload_length = max(slen, dlen);
    req->crp = crp;

    return;
}

在这个req传完之后,就调用

crypto_acomp_compress(struct acomp_req *req)
{
    req->crp->crp_op = CRYPTO_OP_COMPRESS;
    int err = crypto_dispatch(req->crp);
    crypto_destroyreq(req->crp);
    return err;
}

这块应该是最核心的调用部分了,我们应该是没做异步,所以外面的wait_req是个假的函数,如果这个压缩的过程没啥问题,应该就是没啥问题。

image-20240309161454391

这个dispatch没啥问题,那似乎最关键的压缩环节应该是压缩完了的,可能是destroy出问题了?

真奇怪,竟然是访问dlen的时候出问题了

image-20240309162511383

image-20240309162530462

找到问题了,感觉是destroy的东西有点多啊

image-20240309165125252

before的address还是一组kernel的地址,出来之后不是kernel地址了,肯定要报错的,问题是destroy的是req的crp啊,为什么会影响到req本身呢

请教师兄之后,猜测是有地方溢出了,导致栈上有问题,把req错误覆盖了。

那就是捋一遍dispatch能用到的地方,好家伙,我就觉得这个地方很怪

6a12cf42bafe72c3e4118399d0021a8

此处应有甩锅,fanyi写错了哈哈

image-20240311193605919

稍作修改,顺便防止内存泄漏。终于把每次都FATAL的问题解决了

然后就是新的问题,压缩无效,4096大小的数据压缩之后竟然要4101?Exm?

综合我的信息论经验,是我的数据太随机了。。。

image-20240311193836864

降低一下数据随机度,果然能压缩了,大概压缩大小在1200上下,压缩率30%左右,至少做验证是可以了。

用同样的页面测试一下Load

image-20240311194220515

又开始Panic了,排查下

dispatch失败了,又是general protection

image-20240311194504564

已经有点经验了,猜测是哪个指针申请的不对,log出在dispatch中导致的,还是crp的问题

原来是异步的opaque忘记传了,小问题

然后出现了一次死循环,但是这次没log,但是很快就排查到了,因为就一个地方会有死循环,就是callback失败了

image-20240311194657470

最开始用olen当做是否完成的标志,但是如果etype != 0 (出错的时候),这个东西还是会返回一个0,特判一下,如果Error传个负数回去。

image-20240311194812984

最开始丈二和尚摸不到头脑,定义Etype的地方并没有说过22是啥,但是根据我的Kernel经验,22应该是EINVAL,又是crp的参数有问题,但是这次真排查不到了,进别的函数去排查吧

最后定位在opencrypto/cryptosoft.c里

image-20240311195610281

发现这个地方怎么是4096,我Decomp的时候长度应该是一千多的那个数才对

好了,fanyi成功获得第二个锅

image-20240311195656032

这个payload_length就应该是输入的src_len,不能取max,这个对了之后,谢天谢地,功能测试到此结束

644d907cab5829b375edd4e405fccd5

0

评论 (0)

取消