Background As DRAM prices continue to rise, many users are exploring alternative solutions to manage memory more efficiently….
惊人的 IOMMU Overhead 与不合理的 AQC 网卡驱动
背景 给家里的 AMD AI MAX 395 小主机加了个 Thunderbolt 万兆网卡,期望给这台小主机通上 10Gbps 的信息高速公路,这样我可以利用家里的 NAS 的大容量存储快速拉 LLM ,网卡用的是几年前买给 Mac 用的 QNAP…
让树莓派4 WireGuard 性能从 400Mbps 飞涨到 1Gbps
背景 由于学校机房依然需要网络认证,因此我采用了使用 WireGuard 从别处接入网络的方式。为了评估放在学校机房的软路由需要什么样的性能才足够跑满千兆大包 WireGuard ,上周末写了个 WireGuard Benchmark 脚本 来评估 CPU 以及 Kernel 网络栈处理 WireGuard 的性能。为了方便评估以取得更多的结果,我采用 network namspace…
第一次给Kernel邮件列表交Patch遇到的小坑
第一次交 PATCH ,交成了这样: 而我交 patch 的方式只不过是: git format-patch 457391b0380335d5e9a5babdec90ac53928b23b4 –cover-letter # edit cover letter git send-email \ –from…
让Thunderbolt可以使用更大的BAR
问题描述 在Xilinx 7-Series FPGA上用AXI Memory Mapped to PCI Express IP核设置了一个256M的BAR,这个BAR直接连接到主板的PCIe上可以正常进行MMIO,但将FPGA通过Thunderbolt硬盘盒+M.2转PCIe连接到Thunderbolt上却无法使用。 通过查阅dmesg可观测到如下报错信息: [ 246.677146] pci 0000:0c:00.0: BAR 0: no…
Draco(MICRO'20) Architectural and Operating System Support for System Call Security 阅读笔记
要解决的问题 Linux内核提供了seccomp来对syscall权限进行检查,被广泛应用在容器、沙盒等多种场景,例如Docker和Android。但原版seccomp的实现是通过BPF对定义的规则一条条进行检查,如下图所示,十分低效: 同时,作者也进行了一系列的benchmark发现这样的检查带来了较大的开销: 其中,左边的docker-default指的是Docker默认使用的seccomp-profile,syscall-noargs指的是使application-specific profile但只检查syscall id而不检查参数,而syscall-complete就在noargs的基础上加上参数检查,最后加上-2x的后缀表示的是这些检查复制2次,来建模更复杂的安全检查场景。 可以看出,seccomp检查带来的开销即使是对于像nginx这样的macro-benchmarks也是比较大的,但现有的seccomp使用BPF,规则十分灵活,因此需要针对这种灵活的规则设计一种加速的方法。 Observation 检查系统调用ID会给Docker容器中的应用带来明显(noticeable)的性能开销。 检查系统调用参数比只检查系统调用ID的开销大得多(significantly more expensive)。 规则加倍,开销加倍。 syscall with 相同id和参数存在局部性(下图指明了实验中的距离) 解决方法 为了解决这个问题,作者引入了一个Fast…
在Xilinx FPGA上搭建SoC
背景 之前出于科研需求和龙芯杯比赛需求都自己搭建过SoC来运行CPU软核,整理一下我的搭建SoC的流程。以下的讨论基于MIPS和RISC-V两种软核ISA为例。 大家也可以参考我搭建的两个SoC以及对应的软件: cyysoc (RISC-V Rocket+Coherent DMA) pblaze_soc 某大船SSD上的xc7k325t-2 FPGA+2G 72bit DDR3 ECC 用了某个PCI-E引出到SFP接口的扩展板,连接光模块/电口模块提供网络 PCI-E和SFP的IO在FPGA上都是GTX 用了某个该板子的IO接口转USB UART的扩展板 扩展板上面还有rgmii…
调试sfence.vma,12天之差错过Linux Patch一个
起因 最近造了个RISC-V CPU模拟器——CEMU,并实现了软件模拟TLB来加速地址翻译。而TLB和页表并不自带一致性,在RISC-V上需使用sfence.vma这样的指令来刷掉TLB和Page Table Cache(如果存在)和硬件流水线上的指令来确保页表修改生效。并通过了RISC-V Test中的所有测试,然而在启动Linux进入Busybox却遇到如下问题: 尽管Busybox能跑起来并正常使用,但同样的内核在qemu和Rocket上都是完全正常的,因此我继续探索了一下可能的问题。后来objdump了一下busybox的这个地址,发现是一条ret(jalr)指令,而跳转的地址是从栈上ld的。 因此我就怀疑是页表出现了问题导致fork的时候这个进程的栈对别的进程的栈产生了破坏。 第一次尝试 我首先怀疑了TLB查找出现问题,因此我修改了sfence.vma指令的实现,将sfence.vma实现为不管asid和va,全刷TLB,结果用户进程正常载入了。 后续继续做了些尝试,发现只要在执行sfence.vma的时候忽略ASID,但保留va的判断,用户进程依然正常运行。 第二次尝试 这次在实验室里twd2和dram一起来看我调试了。我在每次TLB Hit的情况下都重新进行一遍Page Table Walk,并检查Walk出来的PTE结果和TLB的区别。 结果发现Linux Kernel在一个Hart启动的时候会出现一次页表的W位不同的问题,但那是一条sfence.vma指令的fetch,所以没有对运行结果产生影响。…
U-Boot RISC-V M Mode的灵车SMP启动
为了方便运行任意M Mode软件,同时不想自制Bootloader。我决定在我的RISC-V SoC上使用M Mode U-Boot。然后再上面跑OpenSBI再把Linux作为OpenSBI的Payload运行。但U-Boot的go命令只会修改当前Hart的PC,并不会让其他Hart进行跳转。这就导致后续OpenSBI启动Linux一发送IPI就不断进入Trap Handler。 最后我想了个简单解决方法是,一旦进入Trap Handler且发现是当前无法解决的Trap就跳转到DRAM_BASE(也就是OpenSBI的TEXT段开始地址),并将a0寄存器设置上hartid(符合OpenSBI要求)。这样当IPI产生时其他核就会进入OpenSBI。配合Rocket Chip开双核,经过多次测试,Linux SMP均正常工作。不过比较灵车,理论上正确做法应该修改U-Boot代码控制其他核的IPI跳转。 修改在此:https://github.com/cyyself/u-boot/commit/c637a978068c434691899f1139b8b3e6c45efc93 在这种方法下,只需要保证设备树科学,然后使用u-boot通过tftp/串口将OpenSBI编译的fw_payload.bin载入到DRAM_BASE,然后使用go 0x80000000即可完成SMP启动。不过这种灵车做法需要确保的是,启动过程发送IPI之前不会对U-Boot使用的内存地址进行任何破坏。
修网(AXI Ethernet)
故障现象 今日发现我之前搭建的基于Rocket Chip的SoC,在U-Boot中网卡工作,但Linux中网卡一直无法工作。并发现经常在Linux中使用ip link set eth0 up在看到dmesg输出3行网卡开启信息后系统就卡死,并在reset核心后U-boot(XIP)载入到开始访问DRAM的位置就停下。看起来像是AXI总线上(因为我没有进行Reset)有请求在我reset期间没有完成,这大概可以说明AXI总线上发了一些核的Slave无法处理的请求。 起初我怀疑问题有一部分和中断有关,因为U-Boot没有使用中断,但通过检查/proc/interrupts发现连接在PLIC上两个网卡的中断号都能在set eth0 up后出现多次中断。然后看了下Linux的Documentation中的设备树相关部分,发现我的网卡中断和DMA中断设置不对(要求把DMA中断也放在网卡中断中,但实测是等价的),修改后问题依旧,按照一个别人能工作的设备树写法修改后问题依旧。 而继续搜索相关资料发现,别人遇到的问题多数都是PHY相关。然而我使用的是SFP接口的1000BaseX,AXI Ethernet会自己实例化一个PHY,并不是外置的。 解决方法 后来我对比了我的SoC和别人的SoC,发现我拖出来的AXI DMA IP核的Memory Map Data…
用Linux resctrl玩x86 CPU上的L3 CAT
最近基于Verilator框架写了一个SoC仿真器,用C++实现了AXI接口以便软件定义MMIO。然而写完后运行发现,它的速度确实比FPGA要慢上非常多,FPGA上几秒钟完成的事情用Verilator仿真可能要跑几分钟才能跑出来。而RTL仿真是对于CPU的Cache压力很大的应用场景,因此就玩了下Linux的resctrl进行测试。方便后续选购CPU。 Linux resctrl已经支持Intel和AMD处理器中的许多技术。包括常见到的用来共享隔离Cache缓解侧信道的Intel CAT技术。然而我手里的Intel 12900K通过查看/proc/cpuinfo发现它只支持L2 CAT。而自己的AMD 5800X却可以支持L3 CAT,因此就用它进行了测试。而resctrl的配置主要参考了User Interface for Resource Control feature。 首先,对于Kernel v5.0以上,需要在config中打开CONFIG_X86_CPU_RESCTRL。Debian已经默认打开。 然后,挂载resctrl文件系统: sudo mount…
使用nandsim模拟超过64G的大容量MTD设备
最近做毕设项目需要模拟mtd设备,并对yaffs进行相关测试。而由于要求模拟1TB容量的mtd设备,而若使用block2mtd,由于它的page size为1B大小,无法进行yaffs的挂载。 后来同学发现了Linux Kernel自带了nandsim模块,我们可以模拟一个块设备。且这个模块启动的时候可以通过overridesize参数设置整个mtd的大小为所选模拟设备的erase_size为2的整数幂。 但我们实测发现,如果overridesize设置过大,会报出以下错误: ➜ sudo modprobe nandsim first_id_byte=0x98 second_id_byte=0xd7 third_id_byte=0x94 fourth_id_byte=0x32 overridesize=20 insmod: ERROR: could not…
解决Clang+LLVM编译的Kernel多余.LBB符号导致Call Trace不方便查阅的问题
最近因为科研需求需要在全志D1开发板做性能的Evaluation,但是这个开发板的驱动用GCC编译是正常,而使用Clang+LLVM编译时存在许多问题,SD卡、网卡、USB全都有问题,因此考虑自己做一些修复。 但是由于硬件不方便在QEMU模拟,开发板的调试全靠串口输出,因此比较困难,但是一个大问题是LLVM编译的内核多了许多.LBB符号,导致Call Trace大概会长这样: [ 2.666348] [<ffffffff80004ecc>] .LBB2_1+0x20/0x2c [ 2.671152] [<ffffffff808f2c64>] dump_stack_lvl+0x4a/0x66 [ 2.676562] [<ffffffff808f2c98>] .LBB4_1+0x10/0x18 [ 2.681360] [<ffffffff808f27ce>]…
使用Debian Preseeding在无VNC控制台的虚拟机上安装Debian
起源 找学长要了一台学校的高配置虚拟机方便运行自动调参内卷机,但由于不提供VNC控制台也不提供Debian镜像,只提供Ubuntu和CentOS,且安装虚拟机内系统还得找服务器厂家的人来进行,我也不知道是啥Hypervisor,因此非常难受,就想自己折腾一下安装。 后来经过Soha指点,可以采用Debian Preseeding来进行,现在汇报研究成果。 前置技能 由于涉及到修改bootloader的配置,需要对当前虚拟机内的Linux发行版使用的bootloader有一定的了解,本文将以grub2为例。 且为了防止遇到一些问题,建议在开始之前先进行切换至root用户。 准备文件 下载当前Debian的安装内核与initrd wget http://mirrors.tuna.tsinghua.edu.cn/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64/initrd.gz wget http://mirrors.tuna.tsinghua.edu.cn/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64/linux 注:如果服务器并非amd64架构,例如arm64,换成对应的架构也是可以的,曾经在华为云的ARM机器上亲测成功 修改initrd 解压initrd mkdir initrd…
使用CONNMARK解决Linux上源进源出问题
FWMARK是啥? FWMARK是Linux在处理网络数据包的时候给数据包加上的一个标识,我们可以根据fwmark配置不同的转发规则。 作用域 FWMARK作用于单个数据包。 比如我们可以采用ip rule add fwmark 0x1234 lookup 456这样,让被mark为0x1234的数据包查询路由表456出去。 由于FWMARK是针对单个PACKET而言的,因此在下文中我们所指的Packet Mark与这里的FWMARK相同。 如何给出去/进来的数据包设置FWMARK? 如果要对服务器外出连接设置fwmark,可以使用socket,可以通过setsockopt来设置数据包的MARK,详见这里。 如果要对于某个Wireguard网卡的流入数据包设置fwmark,可以使用wg set $WG_IFACE…
GDB Watchpoint在x86、MIPS、ARM上的实验与MIPS Illegal instruction
MIPS上遇到的glibc的坑 最近突发奇想可以用硬件提供的Debug功能来做一些简单的安全防护,于是进行了一个小实验测试在不支持硬件Debug的平台上设置GDB的Watchpoint带来的性能影响。但是在MIPS平台上测试的时候遇到了一个小问题。 首先将测试程序通过mipsel-linux-gnu-gcc在其它平台上交叉编译,只添加-static -g用于使用静态编译以及开启调试,并在x86上使用qemu-user-static测试通过,然后通过sftp传到MIPS平台上(手边除了FPGA的软核只有一台使用mt7621CPU的路由器为MIPS架构硬件,因此选择了我的路由器作为实验环境),结果发生了以下一幕。 root@OpenWrt:/tmp/gdb-perf# file a.out a.out: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV),…
基于块设备的Linux内核内存监视模块
就在完成了支持Linux 5.10的ramdisk驱动模块后不久,突发奇想,既然之前提到Linux内核模块之间地址空间是完全共享的,那么可以编写一个模块,通过块设备驱动的方式来直接对内存进行读写。 因此,我便对之前块设备驱动的代码做了些许改动,编写了一个新的模块,名为spy。 于是我便尝试从新的模块对之前的ramdisk模块进行一个数据盗取。 获取数据的地址 Linux内核本身提供了一个查看内核符号的接口,位于/proc/kallsyms,我们只需要将它cat出来然后用grep选择我们所需的数据即可。 $ sudo cat /proc/kallsyms | grep myramdisk ffffffffc0bd8024 r _note_7 [myramdisk] ffffffffc0bd7000…
自己动手写支持Linux 5.10的ramdisk驱动模块
major与minor 首先,我们需要了解Linux下块设备的major与minor的概念。 对于Linux用户,可以尝试执行在自己的电脑上执行一下ls -l /dev | grep -E 'nvme|sd。输出大概会长这样: crw——- 1 root root 250, 0 Mar 9 13:25…
Linux内核调试环境的搭建 QEMU+GDB
最近在折腾Linux内核,但是折腾调试环境以及修改内核的编译选项就折腾了我一晚上的时间。 我使用的调试环境包括: QEMU(用于软件模拟系统执行环境,方便对硬件各寄存器等状态进行调试,为了方便起见,这里使用x86_64环境) GDB 以下是我的折腾步骤: Step 0. 安装必要工具 需要确保你已经有以下软件包(对于Debian/Ubuntu): build-essential gdb qemu-system-x86 rsync(用于带权限的文件复制) 如果你采用其他发行版,可以参考对应发行版的相关Wiki,以及使用软件包管理器的搜索功能。 Step 1. 准备内核 Substep…