summaryrefslogtreecommitdiff
path: root/文档/uintr-event.md
diff options
context:
space:
mode:
Diffstat (limited to '文档/uintr-event.md')
-rw-r--r--文档/uintr-event.md192
1 files changed, 192 insertions, 0 deletions
diff --git a/文档/uintr-event.md b/文档/uintr-event.md
new file mode 100644
index 0000000..489fb42
--- /dev/null
+++ b/文档/uintr-event.md
@@ -0,0 +1,192 @@
+## 问题与思路
+
+问题:
+
+- 读者写者问题, 共享内存, eventfd+epoll 通知
+- 多核 CPU, 读写进程可能在不同核心.上下文切换成本更高.
+
+使用 uintr 解决问题的思路:
+
+思路1
+
+- uintr 无法直接干涉 读者进程的调度,但借鉴 `uintr_receiver_wait` 的思路,可以节省一些 写者发通知的成本,缓解这个问题.
+- 写者调用 `_senduipi(uipi_index);` 后, 硬件会比较中断向量与寄存器(IA32_UINTR_MISC 的 39:32 bit),是否一致,不一致则正常触发内核中断. 进入内核中断处理后, 可以对接到 eventfd 等机制, 完成通知传递.
+- 在上述过程中,写者发通知成本由硬件执行, 因此有可能比写者进程直接通过系统调用发通知更快.
+
+思路2
+
+- 完全放弃 eventfd + epoll 的形式,改为使用 中断形式处理通知. uintr 有可能带来数倍的性能提升.
+- 现有实现中,当读者进程挂起时, 用户中断会触发一次系统中断,进入系统中断,记录未被执行的用户中断(称为虚假用户中断).
+- 针对读者进程挂起问题, 有可能在 虚假用户中断中,类似 思路 1,直接唤醒读者.
+ - [poc_v2](https://github.com/intel/uintr-linux-kernel/blob/uintr-next/arch/x86/kernel/irq.c#L381) 有类似的处理.
+
+思路2 改造已有业务涉及范围太大, 先实现并评估 思路 1.
+
+## 设计与实现
+
+要求: 按照思路 1,实现基于 uintr 的通知机制
+
+- 适配 epoll 管理,方便使用.
+- 用户中断不应干扰读者进程其他处理流程.
+
+参考 `uintr_receiver_wait` 的思路,使用 eventfd 作为通知机制.
+
+- 增加新的系统中断 及 中断处理函数. 在中断处理函数中写入 eventfd 通知.
+- 增加新的系统调用, 创建应用层和内核的关联.
+- 调整默认用户中断行为,防止干扰读者进程其他事务.
+
+### 代码实现
+
+源码在 [epoll_waitlist](https://git.mesalab.cn/zhangyang/uintr-linux-kernel/-/tree/epoll_waitlist?ref_type=heads) 分支.
+
+[add UINTR_EVENT_VECTOR](https://git.mesalab.cn/zhangyang/uintr-linux-kernel/-/commit/d1528589b422729a8f9b10fce153425bc8ed5949): 增加新的系统调用 和 中断处理函数, 声明 `UINTR_EVENT_VECTOR` 地方比较多,参考 `UINTR_KERNEL_VECTOR` 小心添加.
+
+```c
+#define UINTR_EVENT_VECTOR  0xe9
+
+DEFINE_IDTENTRY_SYSVEC(sysvec_uintr_event_notification)
+{
+ ack_APIC_irq();
+ inc_irq_stat(uintr_event_notifications);
+ /* do event things */
+ uintr_event_write();
+}
+```
+
+[add system call uintr_event(int fd)](https://git.mesalab.cn/zhangyang/uintr-linux-kernel/-/commit/c6c83e45719d3774bf723536f323e317eb9a384b): 增加新的系统调用, 读者进程首先创建 eventfd 的文件描述符,再通过 `uintr_event` 调用传递给内核.
+
+最终内核维护 [eventfd_ctx 和 等待 list](https://git.mesalab.cn/zhangyang/uintr-linux-kernel/-/blob/epoll_waitlist/arch/x86/kernel/uintr_core.c#L62)
+
+```c
+/* for uint_event*/
+static DEFINE_SPINLOCK(uintr_event_lock);
+static struct eventfd_ctx *uintr_event_ctx = NULL;
+static struct list_head uintr_event_list = LIST_HEAD_INIT(uintr_event_list);
+```
+
+- 通过 uintr_event_ctx 关联到应用层通知.
+- 收到 uintr-evet 用户中断时,中断处理程序调用 `uintr_event_write`,写入一个字节,通知读者进程.
+- `uintr_event_write` 还有一个重要流程是通过 `uintr_event_list` 置位对应 UPID 的 ON 位置, 在标准流程中 ON 置位由硬件完成,这里必须软件置位.
+
+[调整默认用户中断行为](https://git.mesalab.cn/zhangyang/uintr-linux-kernel/-/commit/aadb3fd0fd2c1b4dab3e68fc9bee8a98f0afb12c): 注意,这个提交使 uintr 无法再接收用户中断了.
+
+- 读者进程挂起时, 不再置位 SN, [`switch_uintr_prepare`](https://git.mesalab.cn/zhangyang/uintr-linux-kernel/-/blob/epoll_waitlist/arch/x86/kernel/uintr_core.c#L903);
+- 读者进程回到前台时, 不再更换 upid 的通知向量,不再发起 `send_IPI_self` 触发用户中断处理. [`switch_uintr_return`](https://git.mesalab.cn/zhangyang/uintr-linux-kernel/-/blob/epoll_waitlist/arch/x86/kernel/uintr_core.c#L979)
+
+#### qemu bug 修正
+
+可能与 qemu 实现有关,已有代码中不得不处理寄存器丢失置位等情况, 基于硬件测试时请去除相关代码,防止干扰.
+
+ON 置位:
+
+- 问题: 理论上 ON 仅在写者发送时,置位一次, 当在测试中,依然存在无故置位情况, 导致 硬件无法发送用户中断.特别是当读者进程挂起后,发生频率更高.
+- 解决: 可能的地方,多次重置 ON,目前是在 进程切换到后台时,增加一次重置 ON;
+- [代码](https://git.mesalab.cn/zhangyang/uintr-linux-kernel/-/blob/epoll_waitlist/arch/x86/kernel/uintr_core.c#L904)
+
+UINTR_TT 中 uitt 地址无故清空
+
+- 问题: 当写者进程因调度切换运行核心时, UINTR_TT 寄存器重置为全 0.导致 用户中断发送到错误地址.
+- 解决: 用户进程回到前台之前,比较 UINTR_TT 寄存器内 uitt 地址与实际 uitt 地址是否相同,不相同则重写.
+- [代码](https://git.mesalab.cn/zhangyang/uintr-linux-kernel/-/blob/epoll_waitlist/arch/x86/kernel/uintr_core.c#L918)
+
+写者执行 `_senduipi` 但无输出, 过一点时间又恢复正常.
+
+- 问题: 写者进程因调度出现了核心切换,其他核心没有置位 UIF,导致写者在这个核心运行时无法发送用户中断.
+- 解决: 限制 qemu 模拟双核 cpu,在读者 写者进程分别都执行一次 `_stui()`.这个问题应该是 qemu 对 uintr 实现并不完整导致.
+- 在测试代码中,与内核无关.
+
+## 性能测试
+
+测试程序在 [uintr-event](https://git.mesalab.cn/zhangyang/uintr-event) 下的 `/test` 文件夹
+
+- 测试部分源码来自 ipc-bench
+
+比较 uintr-event 和 eventfd 在相同场景下的性能
+
+- 共享内存 epoll 等
+- 取时间,多次复测 重启 qemu; 保证编译命令相同;
+- 写者进程 write 前时间,写入共享内存, 读者唤醒后取结束时间.
+ - 单独测量了 读者挂起,从写者 write 到 读者唤醒,这个场景下时间..
+ - 写者发通知前增加了 100ms 的延迟,以等待读者挂起.(100ms 大于任何已测得的数据)
+- 跳过失败的,只统计成功记录.
+
+编译命令如下
+
+```c
+gcc-11 -O2 -Wall -static -muintr -minline-all-stringops -std=c11 ./shm-eventfd-bi.c -lpthread -o ./shm-eventfd-bi -lm
+
+gcc-11 -O2 -Wall -static -muintr -minline-all-stringops -std=c11 ./shm-uintr-event-bi.c -lpthread -o ./shm-uintr-event-bi -lm
+```
+
+非阻塞测试 100000,阻塞测试 10000;
+
+| method | Average(us) | min(us) | max(us) | rate(msg/s) | Standard deviation |
+| ---------------- | ----------- | ------- | --------- | ----------- | ------------------ |
+| eventfd | 93.542 | 20.790 | 34906.550 | 10690 | 298.206 |
+| uintr_event | 89.837 | 31.820 | 7010.990 | 11131 | 60.103 |
+| evetfd_wait | 523.533 | 129.080 | 19615.600 | 1910 | 865.047 |
+| uintr_event_wait | 266.885 | 97.140 | 10011.420 | 3746 | 261.965 |
+
+原始记录
+
+```c
+============ RESULTS ================
+Message size: 1
+Message count: 99575
+Total duration: 9314.517 ms
+Average duration: 93.542 us
+Minimum duration: 20.790 us
+Maximum duration: 34906.550 us
+Standard deviation: 298.206 us
+Message rate: 10690 msg/s
+=====================================
+
+============ RESULTS ================
+Message size: 1
+Message count: 99999
+Total duration: 8983.693 ms
+Average duration: 89.837 us
+Minimum duration: 31.820 us
+Maximum duration: 7010.990 us
+Standard deviation: 60.103 us
+Message rate: 11131 msg/s
+=====================================
+
+============ RESULTS ================
+Message size: 1
+Message count: 9792
+Total duration: 5126.437 ms
+Average duration: 523.533 us
+Minimum duration: 129.080 us
+Maximum duration: 19615.600 us
+Standard deviation: 865.047 us
+Message rate: 1910 msg/s
+=====================================
+
+============ RESULTS ================
+Message size: 1
+Message count: 9999
+Total duration: 2668.588 ms
+Average duration: 266.885 us
+Minimum duration: 97.140 us
+Maximum duration: 10011.420 us
+Standard deviation: 261.965 us
+Message rate: 3746 msg/s
+=====================================
+```
+
+## 结论
+
+基于 qemu 模拟器的数据:
+
+- 接收者非挂起时, uintr-event 与 evenfd 速度相同或稍快一点点.
+- 接收者挂起时, uintr-event 唤醒接收者的速度要快 50% 左右.
+ - 我对这个结论在物理机能否复现存疑, 幅度应该没有这么夸张.
+ - 另外测试程序也需要复核以确保测试结果的准确性.
+
+uintr 结合 eventfd 应当可以作为一个解决 eventfd+epoll 瓶颈的一个思路, 提升效果明显.
+
+- 用户中断 rfc 尚未被通过, 源代码库开发并不活跃 (最后 [commit](https://github.com/intel/uintr-linux-kernel/commit/f20fa04c4553c6140dc8dc49e55fa282d2360ece) 时间是 Sep 13, 2021), 尚不能在生产环境使用.
+- 以上测试结果均基于 qemu 模拟器测得, qemu 的实现尚有一些 bug,无法 100%保证 物理机可以复现相同的结果.
+- uintr_event 基于 [rfc-v1](https://github.com/intel/uintr-linux-kernel/tree/rfc-v1) 修改, 最新的 [poc_v2](https://github.com/intel/uintr-linux-kernel/tree/poc-v2%3E) 跑 ipc-bench 中有 bug (不确定是 qemu 还是 kernel 的问题).
+ - 已有 qemu 要运行,还需要基于这个 [commit](https://github.com/OS-F-4/uintr-linux-kernel/commit/9f6a41b01ad529801838f1bd8175ed76c1509221) 修改 kernel 代码.