Kernel
Threads by month
- ----- 2025 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- 46 participants
- 19070 discussions

09 Jun '25
From: Vikash Garodia <quic_vgarodia(a)quicinc.com>
stable inclusion
from stable-v6.1.135
commit 4e95233af57715d81830fe82b408c633edff59f4
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IC5BIO
CVE: CVE-2025-23159
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id…
--------------------------------
commit f4b211714bcc70effa60c34d9fa613d182e3ef1e upstream.
sfr->buf_size is in shared memory and can be modified by malicious user.
OOB write is possible when the size is made higher than actual sfr data
buffer. Cap the size to allocated size for such cases.
Cc: stable(a)vger.kernel.org
Fixes: d96d3f30c0f2 ("[media] media: venus: hfi: add Venus HFI files")
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue(a)linaro.org>
Signed-off-by: Vikash Garodia <quic_vgarodia(a)quicinc.com>
Signed-off-by: Hans Verkuil <hverkuil(a)xs4all.nl>
Signed-off-by: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>
Signed-off-by: Zizhi Wo <wozizhi(a)huawei.com>
---
drivers/media/platform/qcom/venus/hfi_venus.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c
index 8b1375c97c81..b47c24cdde13 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus.c
+++ b/drivers/media/platform/qcom/venus/hfi_venus.c
@@ -978,18 +978,26 @@ static void venus_sfr_print(struct venus_hfi_device *hdev)
{
struct device *dev = hdev->core->dev;
struct hfi_sfr *sfr = hdev->sfr.kva;
+ u32 size;
void *p;
if (!sfr)
return;
- p = memchr(sfr->data, '\0', sfr->buf_size);
+ size = sfr->buf_size;
+ if (!size)
+ return;
+
+ if (size > ALIGNED_SFR_SIZE)
+ size = ALIGNED_SFR_SIZE;
+
+ p = memchr(sfr->data, '\0', size);
/*
* SFR isn't guaranteed to be NULL terminated since SYS_ERROR indicates
* that Venus is in the process of crashing.
*/
if (!p)
- sfr->data[sfr->buf_size - 1] = '\0';
+ sfr->data[size - 1] = '\0';
dev_err_ratelimited(dev, "SFR message from FW: %s\n", sfr->data);
}
--
2.39.2
2
1
Ian Rogers (1):
perf test bpf-counters: Add test for BPF event modifier
Tengda Wu (2):
perf stat: Support inherit events during fork() for bperf
perf test: Use sqrtloop workload to test bperf event
Veronika Molnarova (1):
perf test stat_bpf_counter.sh: Stabilize the test results
Xiaomeng Zhang (2):
perf stat: Increase perf_attr_map entries
perf stat: Fix incorrect display of bperf when event count is 0
tools/lib/perf/include/perf/bpf_perf.h | 1 +
tools/perf/builtin-stat.c | 1 +
tools/perf/tests/shell/stat_bpf_counters.sh | 79 ++++++++++-----
tools/perf/util/bpf_counter.c | 61 +++++++++---
tools/perf/util/bpf_skel/bperf_follower.bpf.c | 98 +++++++++++++++++--
tools/perf/util/bpf_skel/bperf_u.h | 5 +
tools/perf/util/target.h | 1 +
7 files changed, 198 insertions(+), 48 deletions(-)
--
2.34.1
2
7

[openeuler:OLK-5.10 2953/2953] kernel/locking/lockdep.o: warning: objtool: lockdep_unregister_key()+0x2ff: unreachable instruction
by kernel test robot 09 Jun '25
by kernel test robot 09 Jun '25
09 Jun '25
Hi Waiman,
First bad commit (maybe != root cause):
tree: https://gitee.com/openeuler/kernel.git OLK-5.10
head: dd32d51934d00fd319caec8710bd1a438bd69859
commit: b91170be2b1f1ef673f5e79c7a4beb5673a8f881 [2953/2953] locking/lockdep: Avoid potential access of invalid memory in lock_class
config: x86_64-randconfig-123-20250609 (https://download.01.org/0day-ci/archive/20250609/202506092002.S7MmpM4X-lkp@…)
compiler: clang version 20.1.2 (https://github.com/llvm/llvm-project 58df0ef89dd64126512e4ee27b4ac3fd8ddf6247)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250609/202506092002.S7MmpM4X-lkp@…)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp(a)intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202506092002.S7MmpM4X-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> kernel/locking/lockdep.o: warning: objtool: lockdep_unregister_key()+0x2ff: unreachable instruction
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
1
0
Support xcall prefetch.
Changes in v6:
- Only use one reserved kabi in task_struct for xcall.
- Free prefetch item after eventpoll_release_file() to avoid
possible competition between ep_prefetch_item_enqueue() and pfi free.
- Fix "echo > /proc/xcall/cpu_list" show problem.
Changes in v5:
- Fix dentry free before free_prefetch_item() and then UAF in
prefetch work by cancel_work_sync() before free pfi and move
free_prefetch_item() to the first of __fput().
- Fix __queue_work() oops bug.
- /proc/xcall/mask_list -> /proc/xcall/cpu_list
- Fix xcall_prefetch print data type.
- Update prefetch tracepoint, move xcall points definition
to separete header file.
- Make xcall_prefetch depends on EPOLL.
- Adjust xcall prefetch buf to 16KB by default.
- Add xcall_prefetch_init() function.
- Only use write lock in free_prefetch_item().
- Simplify xcall_read() and add some comment.
- Update commit message.
Changes in v4:
- Enable FAST_SYSCALL/IRQ and XCALL_PREFETCH by default.
- Fix xint sleeping function called from invalid context bug.
- free_prefetch_item() in file_free() instead of filp_close(), which
fix the alloc-queue and free competition.
- Fix kernel_read() warning for FMODE_READ not set.
- Add sock_from_file() limit for prefetch.
- Check NULL for rc_work.
- Simplfy the cpumask interface code, and rename
to "/proc/xcall/mask_list".
- Remove the xcall_cache_pages_order interface.
- tracepoint update: fd -> file.
- Simplify the xcall_read() function again.
- Handle copy_to_user() return value.
- Remove unused XCALL_CACHE_QUEUED.
- Update the commit message.
Changes in v3:
- Add XCALL_PREFETCH config to isolate feature code.
- Split the /proc/xxx interface code out to independent patches, which
will make it clear.
- Update the cpumask interface to "/proc/xcall/numa_mask", and it can
set the numa mask of all numas one time.
- Add xcall select count to make xcall_cache_pages_order adjust safe.
- Introduce xcall_read_start/end() to make it clear.
- Simplify the xcall_read() function.
- Use cpumask_next() instead of get_nth_cpu_in_cpumask() function.
- Use independent cpu select policy function.
- Remove some unnecessary pr_err().
- Update the commit message.
Changes in v2:
- Upadte the xcall prefetch state machine, remove the queued state and
add prefetch, cancel states.
- Remove the pfi lock and use atomic variables.
- Change the 'xcall select' semantics and simplify the code a lot.
- Remove keep_running, remove unrelated code.
- Remove the count in struct read_cache_entry, and use
percpu hit/miss count.
- Remove sync mode, so remove struct read_cache_entry
in struct task_struct.
- Use hash table to find prefetch item for a file, which will not
change the file struct KABI.
- Use rwlock instead of spinlock for hash table.
- Use alloc_page() instead kmalloc() to align 4KB.
- Update the commit message.
Jinjie Ruan (7):
arm64: Introduce Xint software solution
arm64: Add debugfs dir for xint
eventpoll: xcall: Support async prefetch data in epoll
xcall: Add /proc/xcall/prefetch dir for performance tuning
xcall: Add /proc/xcall/cpu_list for performance tuning
xcall: eventpoll: add tracepoint
config: Enable FAST_SYSCALL/IRQ and XCALL_PREFETCH by default
Liao Chen (1):
revert kpti bypass
Yipeng Zou (3):
arm64: Introduce xcall a faster svc exception handling
arm64: Faster SVC exception handler with xcall
xcall: Introduce xcall_select to mark special syscall
arch/Kconfig | 81 +++++
arch/arm64/Kconfig | 2 +
arch/arm64/configs/openeuler_defconfig | 7 +
arch/arm64/include/asm/cpucaps.h | 2 +
arch/arm64/include/asm/exception.h | 3 +
arch/arm64/kernel/asm-offsets.c | 3 +
arch/arm64/kernel/cpufeature.c | 56 ++++
arch/arm64/kernel/entry-common.c | 22 ++
arch/arm64/kernel/entry.S | 183 ++++++++++-
arch/arm64/kernel/syscall.c | 57 ++++
drivers/irqchip/irq-gic-v3.c | 130 ++++++++
fs/eventpoll.c | 409 +++++++++++++++++++++++++
fs/file_table.c | 1 +
fs/proc/base.c | 150 +++++++++
fs/read_write.c | 10 +-
include/linux/fs.h | 35 +++
include/linux/hardirq.h | 5 +
include/linux/irqchip/arm-gic-v3.h | 13 +
include/linux/sched.h | 4 +
include/linux/xcall.h | 19 ++
include/trace/events/xcall.h | 106 +++++++
kernel/fork.c | 25 ++
kernel/irq/debugfs.c | 33 ++
kernel/irq/internals.h | 18 ++
kernel/irq/irqdesc.c | 19 ++
kernel/irq/proc.c | 10 +
kernel/softirq.c | 73 +++++
27 files changed, 1473 insertions(+), 3 deletions(-)
create mode 100644 include/linux/xcall.h
create mode 100644 include/trace/events/xcall.h
--
2.34.1
2
12
Ian Rogers (1):
perf test bpf-counters: Add test for BPF event modifier
Tengda Wu (2):
perf stat: Support inherit events during fork() for bperf
perf test: Use sqrtloop workload to test bperf event
Veronika Molnarova (1):
perf test stat_bpf_counter.sh: Stabilize the test results
Xiaomeng Zhang (2):
perf stat: Increase perf_attr_map entries
perf stat: Fix incorrect display of bperf when event count is 0
tools/lib/perf/include/perf/bpf_perf.h | 1 +
tools/perf/builtin-stat.c | 1 +
tools/perf/tests/shell/stat_bpf_counters.sh | 79 ++++++++++-----
tools/perf/util/bpf_counter.c | 61 +++++++++---
tools/perf/util/bpf_skel/bperf_follower.bpf.c | 98 +++++++++++++++++--
tools/perf/util/bpf_skel/bperf_u.h | 5 +
tools/perf/util/target.h | 1 +
7 files changed, 198 insertions(+), 48 deletions(-)
--
2.34.1
2
7
hulk inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/ICDKL2
--------------------------------
overlayfs has two types of inode, the overlayfs inode generated by
overlayfs and the real inode of the file. When IMA does the measurement,
process_measurement() will try to detect file content changes for files on
a overlayfs filesystem based on the i_version number of the real inode. But
now comparing with value of overlayfs inode, results in always
re-evaluating the file's integrity.
Therefore, ima_collect_measurement() should update iint->iversion with
real_inode iversion. Also, ima_check_last_writer() should compare i_version
base on real_inode.
This patch is based on the implementation of upstream patch (see below Link
tag). Due to merging the pre-patch to resolve conflicts introduces KABI
changes, we don't fix this with the mainline version.
Link: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/comm…
Fixes: b836c4d29f27 ("ima: detect changes to the backing overlay file")
Signed-off-by: Gu Bowen <gubowen5(a)huawei.com>
---
security/integrity/ima/ima_api.c | 2 +-
security/integrity/ima/ima_main.c | 7 +++++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index d88d7fb9f9a5..69bb71331e3f 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -278,7 +278,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
* which do not support i_version, support is limited to an initial
* measurement/appraisal/audit.
*/
- i_version = inode_query_iversion(inode);
+ i_version = inode_query_iversion(real_inode);
hash.hdr.algo = algo;
/* Initialize hash digest to 0's in case of failure */
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index c211a2c5f297..efa20bd18b97 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -222,16 +222,19 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
{
fmode_t mode = file->f_mode;
bool update;
+ struct inode *real_inode;
if (!(mode & FMODE_WRITE))
return;
mutex_lock(&iint->mutex);
if (atomic_read(&inode->i_writecount) == 1) {
+ real_inode = d_real_inode(file_dentry(file));
+
update = test_and_clear_bit(IMA_UPDATE_XATTR,
&iint->atomic_flags);
- if (!IS_I_VERSION(inode) ||
- !inode_eq_iversion(inode, iint->version) ||
+ if (!IS_I_VERSION(real_inode) ||
+ !inode_eq_iversion(real_inode, iint->version) ||
(iint->flags & IMA_NEW_FILE)) {
iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
iint->measured_pcrs = 0;
--
2.25.1
2
1
hulk inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/IC88OM
--------------------------------
Add user manual for the MPAM features.
Signed-off-by: Zeng Heng <zengheng4(a)huawei.com>
---
Documentation/arch/arm64/mpam.md | 587 +++++++++++++++++++++++++++++++
1 file changed, 587 insertions(+)
create mode 100644 Documentation/arch/arm64/mpam.md
diff --git a/Documentation/arch/arm64/mpam.md b/Documentation/arch/arm64/mpam.md
new file mode 100644
index 000000000000..60f62cd7f781
--- /dev/null
+++ b/Documentation/arch/arm64/mpam.md
@@ -0,0 +1,587 @@
+MPAM 用户手册
+=========
+
+# 1 MPAM 简介
+
+MPAM(Memory system component Partioning and Monitoring)是Arm Architecture v8.4的拓展特性。用于解决服务器系统中,混合部署不同类型业务时,由于共享资源的竞争(L3/L2 Cache,MATA),而带来的关键应用性能下降或者系统整体性能下降问题。
+
+MPAM的应用可针对不同业务,将同时作用于硬件访存路径上产生的竞争和冲突进行隔离控制,从而帮助提升服务器利用率,降低服务部署成本。
+
+本手册只适用于OLK-6.6软件版本。
+
+# 2 内核编译选项
+
+配置 CONFIG_ARM64_MPAM=y 后,即使能MPAM完整功能。
+
+# 3 内核启动参数
+
+启动流程默认关闭MPAM,启动MPAM初始化需要**cmdline**添加**arm64.mpam**参数配置后重启机器。
+
+# 3 接口总览
+
+MPAM功能通过resctrl文件系统呈现,挂载点位于 */sys/fs/resctrl* 。系统启动后,需要手动挂载resctrl文件系统。
+
+## 3.1 系统挂载参数
+
+resctrl可以通过添加挂载参数支持多种挂载方式,具体指令如下:
+
+~~~
+# mount -t resctrl resctrl [-o cdp[,cdpl2][,debug][,l2]] /sys/fs/resctrl
+~~~
+
+挂载参数包括:
+
+* cdp: 针对L3缓存,根据访问指令和访问数据分别配置。
+* cdpl2: 针对L2缓存,根据访问指令和访问数据分别配置。
+* debug: 使能调试接口访问。
+* l2: 使能L2缓存配置和监控功能,默认关闭MPAM L2功能。
+
+## 3.2 resctrl 系统目录介绍
+
+### 3.2.1 Info 目录
+
+Info 目录包含有关已启用资源的信息,每个资源都有其自己的子目录,子目录的名称反映了资源的名称。
+
+每个子目录包含以下与分配相关的文件:
+
+缓存资源(L3/L2)子目录包含以下与分配相关的文件:
+
+**num_closids**: 适用于该资源的有效CLOSID(Class of Service ID)数量。内核会以所有已启用资源中最小的CLOSID数量作为限制。
+
+**cbm_mask**: 适用于该资源的有效位掩码(bitmask)。
+
+**min_cbm_bits**: 写入掩码时必须设置的连续位的最小数量。
+
+**shareable_bits**: 与其他执行实体共享资源的位掩码。用户在设置独占缓存分区时可以使用此字段。
+
+**bit_usage**: 标注了资源所有实例的使用情况的容量位掩码。说明如下:
+
+ * *0*: 对应区域未使用。当系统的资源已被分配,且在“bit_usage”中发现“0”时,这表明资源被浪费了。
+
+ * *H*: 对应区域仅由硬件使用,但可供软件使用。如果资源的“shareable_bits”中有位被设置,但这些位并未全部出现在资源组的分配方案中,则“shareable_bits”中出现但未分配给资源组的位将被标记为“H”。
+
+ * *X*: 对应区域可供共享,并被硬件和软件使用。这些位同时出现在“shareable_bits”和资源组的分配中。
+
+ * *S*: 对应区域由软件使用,并可供共享。
+
+ * *E*: 对应区域被一个资源组独占使用,不允许共享。
+
+**sparse_masks**: 指示是否支持CBM(Capacity Bit Mask)中的非连续1值。
+
+ * *0*: 仅支持CBM中的连续1值。
+
+ * *1*: 支持CBM中的非连续1值。
+
+MB(Memory bandwidth,内存带宽)子目录包含以下与分配相关的文件:
+
+**min_bandwidth**: 用户可以请求的最小内存带宽百分比。
+
+**bandwidth_gran**: 内存带宽百分比分配的粒度。分配的带宽百分比会四舍五入到硬件上可用的下一个控制步长。可用的带宽控制步长为:
+~~~
+min_bandwidth + N * bandwidth_gran
+~~~
+
+**delay_linear**: 指示延迟刻度是线性还是非线性的。该字段仅用于信息参考。
+
+如果支持监控功能,则会存在一个名为 L3_MON 和 MB_MON 的目录,其中包含以下文件:
+
+**num_rmids**: 可用的RMID(Resource Monitoring ID)数量。这是可以创建的“CTRL_MON”+“MON”组的最大数量。
+
+**mon_features**: 如果为该资源启用了监控功能,则列出监控事件。例如:
+~~~
+# grep . /sys/fs/resctrl/info/*_MON/mon_features
+/sys/fs/resctrl/info/L3_MON/mon_features:llc_occupancy
+/sys/fs/resctrl/info/MB_MON/mon_features:mbm_total_bytes
+~~~
+
+**max_threshold_occupancy**: 读/写文件,提供一个最大值(以字节为单位),低于此设定值下,之前使用过的LLC_occupancy计数器可以被考虑重新分配使用。
+
+> 请注意,一旦释放了RMID(资源监控ID),它可能不会立即可用,因为RMID仍然与之前使用该RMID的缓存行相关联。因此,这些RMID会被放入一个“待定”列表中,并在缓存占用量降低后再次检查。如果系统中存在大量处于“待定”状态的RMID,但它们尚未准备好被使用,用户在执行mkdir操作时可能会看到-EBUSY错误。
+
+> max_threshold_occupancy是一个用户可配置的值,用于确定在什么占用量下可以释放一个RMID。
+
+最后,在 Info 目录的顶层有一个名为 **last_cmd_status** 的文件。每次通过文件系统发出“命令”(例如创建新目录或写入任何控制文件)时,该文件都会被重置。如果命令成功,文件内容将显示为“ok”。如果命令失败,它将提供比文件操作错误返回更详细的信息。例如:
+~~~
+# echo "MB:1=110" > schemata
+-bash: echo: write error: Invalid argument
+# cat /sys/fs/resctrl/info/last_cmd_status
+MB value 110 out of range [0,100]
+~~~
+
+### 3.2.2 资源分配和监控
+
+资源组在resctrl文件系统中以目录的形式表示。默认组是根目录,刚挂载后,它拥有系统中的所有任务和CPU,并且可以充分利用所有资源。
+
+在支持RDT(资源分配技术)控制功能的系统中,可以在根目录下创建额外的目录,这些目录指定了每种资源的不同数量(参见下面的“schemata”)。根目录和这些额外的顶级目录在下文中被称为“CTRL_MON”组。
+
+在支持RDT监控的系统中,根目录和其他顶级目录中包含一个名为“mon_groups”的目录,在其中可以创建额外的目录来监控其父“CTRL_MON”组中任务的子集。这些在本文档的其余部分被称为“MON”组。
+
+删除一个目录会将其所代表的组拥有的所有任务和CPU移动到其父目录。删除一个创建的“CTRL_MON”组将自动删除其下所有的“MON”组。
+
+支持将“MON”组目录移动到一个新的父“CTRL_MON”组,以便在不影响其监控数据或分配的任务的情况下更改“MON”组的资源分配。此操作不适用于监控CPU的“MON”组。目前,除了简单地重命名“CTRL_MON”或“MON”组之外,不支持其他任何移动操作。
+
+所有组包含以下文件:
+
+**tasks**: 读取此文件将显示属于该组的所有任务列表。将任务ID写入该文件会将任务添加到该组中。可以通过用逗号分隔任务ID来添加多个任务。任务将按顺序分配。在尝试分配任务时遇到的任何单个失败都会导致操作中止,而在失败之前已添加到组中的任务将保留在组中。失败信息将记录到/sys/fs/resctrl/info/last_cmd_status。
+
+如果该组是一个“CTRL_MON”组,则任务将从之前拥有该任务的“CTRL_MON”组中移除,同时也会从任何拥有该任务的“MON”组中移除。如果该组是一个“MON”组,则任务必须已经属于该组的“CTRL_MON”父组。任务将从任何之前的“MON”组中移除。
+
+**cpus**: 读取此文件将显示该组拥有的逻辑CPU的位掩码。将掩码写入该文件将向该组添加或移除CPU。与“tasks”文件类似,维护了一个层级结构,其中“MON”组只能包含其父“CTRL_MON”组拥有的CPU。
+
+**cpus_list**: 与“cpus”类似,但使用CPU范围而不是位掩码。
+
+当启用控制功能时,所有“CTRL_MON”组还将包含以下文件:
+
+**schemata**: 列出该组可用的所有资源。每种资源都有自己的行和格式——详细信息请参见下文。
+
+**size**: 类似于“schemata”文件的显示,但显示的是每种资源分配的字节大小,而不是表示分配的位。
+
+**mode**: 资源组的“mode”决定了其分配的共享方式。“shareable”资源组允许共享其分配,而“exclusive”资源组则不允许。
+
+**ctrl_hw_id**: 仅在启用调试选项时可用。硬件用于控制组的标识符。在arm64架构上,即是 PARTID。
+
+当启用监控功能时,所有“MON”组还将包含以下文件:
+
+**mon_data**: 包含一组按L3域和MB域事件组织的文件。例如,在具有两个L3域的系统中,将存在子目录“mon_L3_00”和“mon_L3_01”。
+
+每个子目录中都有一个文件对应每个事件(例如“llc_occupancy”、“mbm_total_bytes”)。在“MON”组中,这些文件提供了组中所有任务的当前事件值。在“CTRL_MON”组中,这些文件提供了“CTRL_MON”组中所有任务以及所有“MON”组中任务的总和。有关使用方法的更多详细信息,请参见示例部分。
+
+**mon_hw_id**: 仅在启用调试选项时可用。硬件用于监控组的标识符。
+
+以下为resctrl文件系统目录树:
+
+~~~
+/sys/fs/resctrl(根分组)
+ ├── cpus # bitmask方式显示根分组关联的vcpu
+ ├── cpus_list # cpu list方式显示根分组关联的vcpu
+ ├── ctrl_hw_id # 硬件用于控制组的标识符
+ ├── info # 用于显示属性信息及错误提示信息
+ │ ├── L3
+ │ │ ├── bit_usage # 标注了资源所有实例的使用情况的容量位掩码
+ │ │ ├── cbm_mask # 系统所支持的最大cache way bitmask,一个bit代表一个cache way
+ │ │ ├── min_cbm_bits # 使用schemata所能配置的最小cache way bitmask
+ │ │ ├── num_closids # L3能够提供创建控制组的最大数量
+ │ │ ├── shareable_bits # 当前所有cbm_mask全部shareable,支持后续扩展
+ │ │ └── sparse_masks # 指示是否支持CBM(Capacity Bit Mask)中的非连续1值
+ │ ├── L3_MON
+ │ │ ├── max_threshold_occupancy # 低于此设定值下,之前使用过的LLC_occupancy计数器可以被考虑重新分配使用
+ │ │ ├── mon_features # 列出监控事件
+ │ │ └── num_rmids # 可创建控制组和监控组的总数
+ │ ├── last_cmd_status # 操作错误提示
+ │ ├── MB
+ │ │ ├── bandwidth_gran # 带宽百分比配置粒度
+ │ │ ├── delay_linear # 指示延迟刻度是线性还是非线性的
+ │ │ ├── min_bandwidth # 最小带宽配置百分比
+ │ │ └── num_closids # 同L3 num_closid
+ │ └── MB_MON
+ │ ├── mon_features # 同L3 mon_features
+ │ └── num_rmids # 同L3 num_rmids
+ ├── mode # 资源组的 mode 决定了其分配的共享方式
+ ├── mon_data
+ │ ├── mon_L3_01 # 标号代表L3 cache id
+ │ │ └── llc_occupancy # 表示当前分组所关联的pid/vcpu在该区域上实际占用L3 Cache大小,下同
+ │ ├── mon_L3_122
+ │ │ └── llc_occupancy
+ │ ├── mon_MB_00 # 标号代表numa id
+ │ │ └── mbm_total_bytes # 表示当前分组所关联的pid/vcpu在该区域上内存带宽流量大小,下同
+ │ └── mon_MB_01
+ │ └── mbm_total_bytes
+ ├── mon_groups # 创建监控组目录
+ ├── mon_hw_id # 硬件用于监控组的标识符
+ ├── schemata # 资源使用配置接口
+ ├── size # 显示的是每种资源分配的字节大小
+ └── tasks # 显示与根组关联的pid
+~~~
+
+### 3.2.3 控制组配置接口 Schemata 文件
+
+**schemata**文件中的每一行描述一种资源。每行以资源的名称开头,后面跟着该资源在系统中每个实例上要应用的具体值。
+
+#### Cache IDs
+
+在当前一代的系统中,每个插槽(socket)有一个L3缓存,而L2缓存通常仅由一个核心上的超线程共享,但这并不是架构上的强制要求。我们可能会在一个插槽上有多个独立的L3缓存,或者多个核心共享一个L2缓存。因此,我们不使用“插槽”或“核心”来定义共享资源的逻辑CPU集合,而是使用“Cache ID”(缓存ID)。
+
+在给定的缓存级别上,这将在整个系统中是一个唯一的数字(但不能保证是一个连续的序列,可能会有间隔)。要查找每个逻辑CPU的ID,请查看 /sys/devices/system/cpu/cpu*/cache/index*/id。
+
+#### Cache Bit Masks(CBM,缓存位掩码)
+
+对于缓存资源,我们使用位掩码来描述可用于分配的缓存部分。掩码的最大值由每种CPU型号定义(并且可能因不同的缓存级别而异)。该值在resctrl文件系统的“info”目录中提供,位于info/{resource}/cbm_mask。
+
+#### L3 缓存配置
+
+当未启用CDP(代码/数据缓存划分)时,L3 schemata的格式为:
+
+~~~
+L3:<cache_id0>=<cbm>;<cache_id1>=<cbm>;...
+~~~
+
+当启用CDP时,L3控制被拆分为两个独立的资源,因此您可以分别为代码和数据指定独立的掩码,如下所示:
+
+~~~
+L3DATA:<cache_id0>=<cbm>;<cache_id1>=<cbm>;...
+L3CODE:<cache_id0>=<cbm>;<cache_id1>=<cbm>;...
+~~~
+
+读取schemata文件将显示所有域上所有资源的状态。在写入时,您只需要指定您希望更改的值。
+
+例如使用默认方式挂载,设置L3缓存位掩码只有4位:
+
+~~~
+# mount -t resctrl resctrl /sys/fs/resctrl/
+# cat /sys/fs/resctrl/schemata
+L3:1=fffffff;122=fffffff
+
+# echo "L3:122=3c0;" > /sys/fs/resctrl/schemata
+# cat /sys/fs/resctrl/schemata
+L3:1=fffffff;122=00003c0
+~~~
+
+使用开启CDP方式挂载,设置L3 data缓存位掩码只有4位:
+
+~~~
+# mount -t resctrl resctrl /sys/fs/resctrl/ -o cdp
+# cat /sys/fs/resctrl/schemata
+L3DATA:1=fffffff;122=fffffff
+L3CODE:1=fffffff;122=fffffff
+
+# echo "L3DATA:122=3c0;" > schemata
+# cat /sys/fs/resctrl/schemata
+L3DATA:1=fffffff;122=00003c0
+L3CODE:1=fffffff;122=fffffff
+~~~
+
+#### L2 缓存配置
+
+L2 缓存配置功能默认关闭,需要通过显式添加 l2 挂载参数,才会使能L2缓存配置功能。使能L2功能以后,系统关闭 cpuidle powerdown 功能和cpu下线功能。
+
+L2 schemata的格式是:
+
+~~~
+L2:<cache_id0>=<cbm>;<cache_id1>=<cbm>;...
+~~~
+
+使用“cdpl2”挂载选项可以在L2上支持CDP:
+
+~~~
+L2DATA:<cache_id0>=<cbm>;<cache_id1>=<cbm>;...
+L2CODE:<cache_id0>=<cbm>;<cache_id1>=<cbm>;...
+~~~
+
+L2 缓存配置示例,设置L2缓存位掩码只有4位:
+
+~~~
+# mount -t resctrl resctrl /sys/fs/resctrl/ -o l2
+# cat schemata
+L2:4=000ff;8=000ff
+
+# echo "L2:4=f;" > schemata
+# cat schemata
+L2:4=0000f;8=000ff
+~~~
+
+使用“cdpl2”挂载选项:
+
+~~~
+# mount -t resctrl resctrl /sys/fs/resctrl/ -o l2cdp,l2
+# cat schemata
+L2DATA:4=000ff;8=000ff
+L2CODE:4=000ff;8=000ff
+
+# echo "L2DATA:4=f;" > schemata
+# cat schemata
+L2DATA:4=0000f;8=000ff # 控制组 L2 DATA 只能使用L2 cache4的4个cache way
+L2CODE:4=000ff;8=000ff
+~~~
+
+#### MB 内存带宽分配
+
+对于内存带宽资源,默认情况下,用户通过指定总内存带宽的百分比来控制该资源。
+
+每种CPU型号的最小带宽百分比值是预定义的,可以通过info/MB/min_bandwidth查询。分配的带宽粒度也取决于CPU型号,可以在info/MB/bandwidth_gran中查询。可用的带宽控制步长为:min_bw + N * bw_gran。中间值会被四舍五入到硬件上可用的下一个控制步长。
+
+MB schemata的格式是:
+
+~~~
+MB:<cache_id0>=bandwidth0;<cache_id1>=bandwidth1;...
+~~~
+
+MB 带宽配置示例:
+
+~~~
+# cat /sys/fs/resctrl/schemata
+MB:0=0000100;1=0000100
+
+# echo "MB:0=50" > /sys/fs/resctrl/schemata
+# cat /sys/fs/resctrl/schemata
+MB:0=0000050;1=0000100 # 降低控制组 MB 内存带宽使用上限为50%
+~~~
+
+### 3.2.4 资源分配规则
+
+当一个任务正在运行时,以下规则定义了它可用的资源:
+1. 如果任务属于一个非默认组,则使用该组的分配方案(schemata)。
+2. 否则,如果任务属于默认组,但运行在一个被分配给某个特定组的CPU上,则使用该CPU所属组的分配方案。
+3. 否则,使用默认组的分配方案。
+
+### 3.2.5 监控组配置方法
+
+读取控制组和监控组的监控数据,可通过 mon_data 目录接口读取监控数据:
+
+~~~
+# grep . mon_data/*/*
+mon_data/mon_L3_01/llc_occupancy:73276416
+mon_data/mon_L3_122/llc_occupancy:11875328
+mon_data/mon_MB_00/mbm_total_bytes:32806
+mon_data/mon_MB_01/mbm_total_bytes:31700
+~~~
+
+其中,mon_data读取监控数据文件分别:
+ * llc_occupancy 代表 L3缓存当前占用量,单位 Byte
+ * mbm_total_bytes 代表内存带宽瞬时流量,单位 MB/s
+
+支持在控制组下创建子监控组,监控父控制组监控对象的子集:
+
+~~~
+# cd /sys/fs/resctrl/p1
+# cd mon_groups/ && mkdir m1 # 监控组只能监控,m1分组资源配置跟随p1分组
+# echo '0-1' > cpus_list
+# grep . mon_data/mon_*/*
+mon_data/mon_L3_01/llc_occupancy:18432
+mon_data/mon_L3_122/llc_occupancy:1024
+mon_data/mon_MB_00/mbm_total_bytes:0
+mon_data/mon_MB_01/mbm_total_bytes:0
+~~~
+
+控制组监控的是控制组本身及所有子监控组的监控值之和。
+
+## 3.3 QoS 增强特性
+
+### 3.3.1 PRI 优先级设置
+
+对共享资源优先级进行配置,包括 L3PRI 和 MBPRI:
+
+~~~
+# cat schemata
+ MBPRI:0=0000007;1=0000007
+ L3PRI:1=0000003;122=0000003
+
+# echo "MBPRI:0=0000003" > schemata
+# cat schemata
+ MBPRI:0=0000003;1=0000007 # 降低控制组 MB numa0的优先级
+ L3PRI:1=0000003;122=0000003
+
+# echo "MBPRI:0=0000003" > schemata
+
+~~~
+
+优先级设置数字越大,即优先级越高,反之,数字越小,优先级越低。
+
+> MBPRI 默认值为 3,MBPRI合法值范围 [0,7]。L3PRI 默认值为 0,L3PRI合法值范围 [0,3]。
+
+### 3.3.2 MIN 限低值设置
+
+共享资源实际使用占比低于设置值,会自动提高对该资源使用优先级,包括 L3MIN 和 MBMIN:
+
+~~~
+# cat schemata
+ MBMIN:0=00100
+ L3MIN:1=00100;5=00100
+
+# echo "MBMIN:0=00050" > schemata
+# cat schemata
+ MBMIN:0=00050
+ L3MIN:1=00100;5=00100
+
+# echo "L3MIN:1=00050" > schemata
+# cat schemata
+ MBMIN:0=00050
+ L3MIN:1=00050;5=00100
+~~~
+
+L3MIN 和 MBMIN 接口接受的输入参数为百分比,即设置值与总资源(内存带宽/缓存占用量)的占比。
+
+> MBMIN 和 L3MIN 默认值为 0,合法值范围都为 [0,100]。
+
+### 3.3.3 HDL 强制隔离设置
+
+当MBHDL=1,限制MB共享资源使用量不能超出MB设置值,若MBHDL=0,则允许空闲情况下,MB共享资源使用量超过MB设置值:
+
+~~~
+# cat schemata
+ MBHDL:0=0000001;1=0000001
+
+# echo "MBHDL:0=0000000" > schemata
+# cat schemata
+ MBHDL:0=0000000;1=0000001 # 关闭MB在numa0上的强制限制功能
+~~~
+
+> MBHDL 默认值为 1,合法值范围 [0,1]。
+
+### 3.3.4 MAX 资源上限设置
+
+设置允许分配的缓存容量的最大百分比,包括 L3MAX 接口:
+
+~~~
+# cat schemata
+ L3MAX:1=00100;5=00100
+
+# echo "L3MAX:1=00050" > schemata
+# cat schemata
+ L3MAX:1=00050;5=00100 # 降低L3分配的缓存最大容量百分比
+~~~
+
+> MB 和 L3MAX 默认值为 100,MB合法值范围 [1,100],L3MAX合法值范围 [0,100]。
+
+## 3.4 外设 IO 流量管控
+
+### 3.4.1 控制组绑定外设
+
+MPAM 提供通过绑定 iommu_group ID,对设备IO流量进行带宽限制和监控。
+
+譬如,控制网卡设备 eno2 ,首先查找该设备 PCI_SLOT 信息:
+
+~~~
+# cat /sys/class/net/eno2/device/uevent | grep PCI_SLOT
+PCI_SLOT_NAME=0000:35:00.1
+~~~
+
+或者通过 ethtool 工具查看 bus-info 信息:
+
+~~~
+# ethtool -i eno2 | grep bus-info
+bus-info: 0000:35:00.1
+~~~
+
+按照设备总线信息,查找到该设备所属的 iommu_group :
+
+~~~
+# find /sys/kernel/iommu_groups/ -name "0000:35:00.1"
+/sys/kernel/iommu_groups/17/devices/0000:35:00.1
+~~~
+
+或者通过 lspci 工具查看 iommu_group 信息:
+
+~~~
+# lspci -vvv -s 0000:35:00.1 | grep "IOMMU group"
+ IOMMU group: 17
+~~~
+
+将查询到的group id 17,通过 tasks 接口绑定到指定的控制组下:
+
+~~~
+# cd /sys/fs/resctrl/p1/
+# echo "iommu_group:17" > tasks
+# cat tasks
+iommu_group:17 # 此时iommu_group 17 已被绑定到控制组p1
+~~~
+
+### 3.4.2 查看外设带宽流量
+
+控制组绑定外设所属的iommu组后,可以查看设备流量带宽:
+
+~~~
+# grep . mon_data/mon_MB_0*/*
+mon_data/mon_MB_00/mbm_total_bytes:0
+mon_data/mon_MB_01/mbm_total_bytes:4230
+~~~
+
+### 3.4.3 配置外设带宽流量
+
+通过MB配置接口,可以实现限制设备的流量带宽:
+
+~~~
+# echo "MB:1=0000001" > schemata
+# cat schemata
+ MB:0=0000100;1=0000000
+
+# grep . mon_data/*/*
+mon_data/mon_MB_00/mbm_total_bytes:0
+mon_data/mon_MB_01/mbm_total_bytes:1208
+~~~
+
+# 4 控制组和监控组配置使用示例
+
+/sys/fs/resctrl 默认为根分组,根分组可以创建若干个控制组,一个控制组既可以关联一组pid/tid,也可以关联一组cpu集合。
+
+创建一个新的控制组,关联 pid/tid:
+
+~~~
+# cd /sys/fs/resctrl/ && mkdir p1
+# cd p1 && echo $$ > tasks # 关联当前shell进程pid到p1组
+# cat tasks # 可查看成功关联的pid
+29190
+29607
+~~~
+
+也可以选择关联 cpu:
+
+~~~
+# cd p1 && echo '0-1' > cpus_list
+# cat cpus_list # 可查看关联的cpu
+0-1
+~~~
+
+查看可创建的控制组和监控组数量:
+
+~~~
+# cat info/L3/num_closids # 可在info对应的资源的目录下查看closid数量,即可以创建控制组的数量
+32
+# cat info/MB/num_closids
+32
+# cat info/L3_MON/num_rmids # 可以创建控制组和监控组的总数量
+128
+# cat info/MB_MON/num_rmids
+128
+~~~
+
+通过配置控制组可达到隔离 L3 Cache/Memory Bandwidth 效果,通过读取对应分组mon_data接口可以获取该组资源占用情况,比如对一个控制组限制L3 Cache使用:
+
+~~~
+# cat info/L3/cbm_mask # 查看info目录下对应资源的属性
+fffffff
+# cat info/L3/min_cbm_bits
+1
+
+# cd /sys/fs/resctrl/p1
+# cat schemata
+ MB:0=0000100;1=0000100
+ L3:1=fffffff;122=fffffff
+
+# echo 'L3:0=1' > schemata # 配置1条cache way给p1分组
+# cat schemata
+ MB:0=0000100;1=0000100 # 若此时该组关联pid/cpu,那么该pid/cpu产生的访存请求只会分配到这条cache
+ L3:1=0000001;122=fffffff
+~~~
+
+对控制组限制MB使用:
+
+~~~
+# cat info/MB/min_bandwidth # 和配置L3类似,也可以查看MB的相关信息
+1
+# cat info/MB/bandwidth_gran
+1 # 可知,配置带宽最小百分比是1%,颗粒度是1%
+# cat schemata
+ MB:0=0000100;1=0000100
+ L3:1=0000001;122=fffffff
+# echo 'MB:0=1' > schemata
+# cat schemata
+ MB:0=0000001;1=0000100
+ L3:1=0000001;122=fffffff
+~~~
+
+支持在控制组下创建子监控组:
+
+~~~
+# cd /sys/fs/resctrl/p1
+# cd mon_groups/ && mkdir m1 # 监控组只能监控,m1分组资源配置跟随p1分组
+# ls m1/
+cpus cpus_list mon_data tasks
+# echo '0-1' > cpus_list
+# cat cpus_list
+0-1
+# grep . mon_data/mon_*/*
+mon_data/mon_L3_01/llc_occupancy:18432
+mon_data/mon_L3_122/llc_occupancy:1024
+mon_data/mon_MB_00/mbm_total_bytes:0
+mon_data/mon_MB_01/mbm_total_bytes:0
+~~~
--
2.25.1
2
1
Ian Rogers (1):
perf test bpf-counters: Add test for BPF event modifier
Tengda Wu (2):
perf stat: Support inherit events during fork() for bperf
perf test: Use sqrtloop workload to test bperf event
Veronika Molnarova (1):
perf test stat_bpf_counter.sh: Stabilize the test results
Xiaomeng Zhang (2):
perf stat: Increase perf_attr_map entries
perf stat: Fix incorrect display of bperf when event count is 0
tools/lib/perf/include/perf/bpf_perf.h | 1 +
tools/perf/builtin-stat.c | 1 +
tools/perf/tests/shell/stat_bpf_counters.sh | 79 ++++++++++-----
tools/perf/util/bpf_counter.c | 61 +++++++++---
tools/perf/util/bpf_skel/bperf_follower.bpf.c | 98 +++++++++++++++++--
tools/perf/util/bpf_skel/bperf_u.h | 5 +
tools/perf/util/target.h | 1 +
7 files changed, 198 insertions(+), 48 deletions(-)
--
2.34.1
2
7
---
.../testing/selftests/xcall_prefetch/Makefile | 37 ++++
.../selftests/xcall_prefetch/echo_client.c | 119 +++++++++++++
.../xcall_prefetch/echo_client_multi | Bin 0 -> 21896 bytes
.../selftests/xcall_prefetch/echo_server.c | 110 ++++++++++++
.../selftests/xcall_prefetch/echo_test.c | 52 ++++++
.../xcall_prefetch/mulit_close_test.c | 48 +++++
.../xcall_prefetch/multi_write_test.c | 77 ++++++++
.../xcall_prefetch/signal_recovery_test.c | 164 ++++++++++++++++++
.../xcall_prefetch/thundering_herd_test.c | 119 +++++++++++++
.../testing/selftests/xcall_prefetch/utils.h | 54 ++++++
.../xcall_prefetch/xcall_prefetch_test.sh | 39 +++++
11 files changed, 819 insertions(+)
create mode 100644 tools/testing/selftests/xcall_prefetch/Makefile
create mode 100644 tools/testing/selftests/xcall_prefetch/echo_client.c
create mode 100755 tools/testing/selftests/xcall_prefetch/echo_client_multi
create mode 100644 tools/testing/selftests/xcall_prefetch/echo_server.c
create mode 100644 tools/testing/selftests/xcall_prefetch/echo_test.c
create mode 100644 tools/testing/selftests/xcall_prefetch/mulit_close_test.c
create mode 100644 tools/testing/selftests/xcall_prefetch/multi_write_test.c
create mode 100644 tools/testing/selftests/xcall_prefetch/signal_recovery_test.c
create mode 100644 tools/testing/selftests/xcall_prefetch/thundering_herd_test.c
create mode 100644 tools/testing/selftests/xcall_prefetch/utils.h
create mode 100755 tools/testing/selftests/xcall_prefetch/xcall_prefetch_test.sh
diff --git a/tools/testing/selftests/xcall_prefetch/Makefile b/tools/testing/selftests/xcall_prefetch/Makefile
new file mode 100644
index 000000000000..5bd509df66e0
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/Makefile
@@ -0,0 +1,37 @@
+CFLAGS += -Wall -Wextra -g
+TEST_GEN_FILES := echo_server echo_client echo_test multi_write_test thundering_herd_test mulit_close_test signal_recovery_test
+
+# enable debug
+# echo_client: CFLAGS += -DDEBUG=1
+# echo_server: CFLAGS += -DDEBUG=1
+# multi_write_test: CFLAGS += -DDEBUG=1
+# thundering_herd_test: CFLAGS += -DDEBUG=1
+# mulit_close_test: CFLAGS += -DDEBUG=1
+# signal_recovery_test: CFLAGS += -DDEBUG=1
+
+all: $(TEST_GEN_FILES)
+
+echo_server: echo_server.c
+ $(CC) $(CFLAGS) $< -o $@
+
+echo_client: echo_client.c
+ $(CC) $(CFLAGS) $< -o $@
+
+echo_test: echo_test.c
+ $(CC) $(CFLAGS) $< -o $@
+
+multi_write_test: multi_write_test.c
+ $(CC) $(CFLAGS) $< -o $@
+
+thundering_herd_test: thundering_herd_test.c
+ $(CC) $(CFLAGS) $< -o $@
+
+mulit_close_test: mulit_close_test.c
+ $(CC) $(CFLAGS) $< -o $@
+
+signal_recovery_test: signal_recovery_test.c
+ $(CC) $(CFLAGS) $< -o $@
+
+.PHONY: all
+
+include ../lib.mk
diff --git a/tools/testing/selftests/xcall_prefetch/echo_client.c b/tools/testing/selftests/xcall_prefetch/echo_client.c
new file mode 100644
index 000000000000..2e3c41b2b14e
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/echo_client.c
@@ -0,0 +1,119 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include "./utils.h"
+
+#define DEBUG
+
+void *client(void *arg)
+{
+ int thread_id = *(int *)arg;
+ int sock = 0;
+ struct sockaddr_in serv_addr;
+ struct epoll_event ev, events[MAX_EVENTS];
+ const char* test_messages[MAX_MSGS];
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ handle_error("client:%d create socket failed\n", thread_id);
+ }
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_port = htons(PORT);
+
+ if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
+ close(sock);
+ handle_error("client:%d invalid address\n", thread_id);
+ }
+ if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {
+ close(sock);
+ handle_error("client:%d connection failed\n", thread_id);
+ }
+
+ debug_printf("Thread %d connected\n", thread_id);
+
+ int epoll_fd = epoll_create1(0);
+ if (epoll_fd == -1) {
+ handle_error("client:%d epoll_create1 failed\n", thread_id);
+ }
+
+ ev.events = EPOLLIN;
+ ev.data.fd = sock;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock, &ev) == -1) {
+ handle_error("client:%d epoll_ctl add sock failed\n", thread_id);
+ }
+
+ test_messages[0] = "hello world.";
+ test_messages[1] = generate_number_string(100);
+ test_messages[2] = generate_number_string(1024);
+ test_messages[3] = generate_number_string(4096);
+
+
+ for (int i = 0; i < MAX_MSGS; i++) {
+ const char* msg = test_messages[i];
+ ssize_t msg_len = strlen(msg);
+ if (send(sock, msg, msg_len, 0) != msg_len) {
+ handle_error("client:%d send failed\n", thread_id);
+ }
+
+ debug_printf("Client:%d send %s\n", thread_id, msg);
+
+ int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
+ if (nfds == -1) {
+ handle_error("client:%d epoll_wait failed\n", thread_id);
+ }
+
+ for (int n = 0; n < nfds; ++n) {
+ if (events[n].data.fd == sock) {
+ char buffer[BUFFER_SIZE] = {0};
+ ssize_t read_bytes = read(sock, buffer, BUFFER_SIZE);
+
+ if (read_bytes <= 0) {
+ close(sock);
+ if (read_bytes == 0) {
+ handle_error("client:%d server disconnected\n", thread_id);
+ } else {
+ handle_error("client:%d read failed\n", thread_id);
+ }
+ } else {
+ debug_printf("Client:%d received %s\n", thread_id, buffer);
+ if (strncmp(msg, buffer, msg_len) != 0) {
+ handle_error("client:%d error: response does not match sent message\n", thread_id);
+ }
+ }
+ }
+ }
+ }
+
+ close(sock);
+ pthread_exit(NULL);
+}
+
+int main()
+{
+ pthread_t threads[NUM_THREADS];
+ int ret;
+
+ for (int i = 0; i < NUM_THREADS; i++) {
+ ret = pthread_create(&threads[i], NULL, client, &i);
+ if (ret != 0) {
+ perror("pthread_create failed");
+ continue;
+ }
+ }
+
+ for (int i = 0; i < NUM_THREADS; i++) {
+ if (threads[i] != 0) {
+ pthread_join(threads[i], NULL);
+ }
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/echo_client_multi b/tools/testing/selftests/xcall_prefetch/echo_client_multi
new file mode 100755
index 0000000000000000000000000000000000000000..36aa08d6e12f6e8ec73b2a7f8eb567225eb0b2f8
GIT binary patch
literal 21896
zcmeHPYjhmNm98GCr5TMLBTKd=Ti9cR9ShLQFKmpBY-{is%MWY|>@4hfq-jY5qZwr$
zevlX&WF?*mWo;G$$@&mO&LIiO$uT*B@G{7}Y-bbJ>=JMun>7jKNWo-{fJ8uG_Ph0H
zwK|&JJvsYpqta2|d%wE(R@JSV?&_}U-sfw+#-V9Ur5v_`k#ug7KpJJn!Dh*TG_obE
z2**p<#VikUF2_WEqaer?>8xWcbxJ%3l<X>q8HZL0m?^4iNR;fdC68O=D9SuSCOe&2
zDcfmLfzVS_<(uVsMLx69a#?>xt#%ZfiW*^8v3x8Yk#S+tUfB^$cJrj&JZY!s1*#B|
zqAH#g6Z&5w{n=?f86hcZx9gO4cG@J&n4(k$r7dqILw`+qo21>qEaAsa)i6_3m3JNN
zC@%loq`G;tEH7I>49b2`REC$;MZ&F%7uH2WHIZ;MwYz5blEpQP7uF_Xwexuus9s+D
z)0kSbp@qTC0AZ>-9s7l>{83)H%qRWs(jWZ%*E_C1J?FB63+}pchWni>DsCnnsyEq?
z4khxZh6>5VPs2ayh<Mx%PGCe|F<$N>nk}tBhWu%dQHf{4D^QSkjHVE7&7yyI7W}0w
z_|Yu*KV`vh$%608f(NtU7iGa0W@+yYS@fyD#*63dEcioN@ZK!=BU$i@EcoIq`06a}
zs?MVScouw9798#wwfQenAkelu7-$bigOTv92J23C#*JVoVC)Vj8F8#D7>UH%%#533
z;V5g1MWaSrk_7_z8MXxy$zVJg=n5hy5o^1}NV0I$NCvu-u_!aTW06RpEg4~5Mppt^
ziDW$5*453rQ^^Eti^LM6g|?x<q(K@H1Fno{h;_n8LOR(MM4h^gcsv$YZn;4N>VYb>
z-4bZ)yd}^c3`ZDMi%tfM*_!6Ht5yf**Df`=1vo`0bfL-3uU%yF3l_4#+O6vYAtP>d
zgcC_4zIFZTNGxh>4Yo!Ml-AJ|i^^69gzC782(|-f2o3)^IQ!F){&f5cjCj(ZKMh<S
z=ctb~<h%~5&Xbkla1otR-jG<lGVG$W+uKqu=lQwpPf|`zqkMQp$CHmA9{ws$M-Em*
z0|7~0@6>f>piJOic|BA3eu+C9MZUt7eD-RA)45K%=D;}%uFkoXF=)ZjATwRC;8RSL
zu@MVS=T(*JC7ut!dj4Hv!RdUhQlkau^9-5cx8T$Ul{Q&$Dnq4h7F@-G@^)Bo1l~-Y
z7F<qYcwV;!ms28+@3!EDrWeNcTkv8Fe$axKSnxv@T#X^hzR!Y}TJ(=vaO?T<+ZJ37
z1#WfBg41<VrN=G!WCel_Sn#P9{FDW!bEZmXEO?~?C+I{3CL%Bqfxn>$yjL>gBY)3%
zr@v4Cc^zZ^-hreebH?BEg7bMEh0LOlY8e~O%=s9kWF{m1PRbb`dLxs`9O5!vn1_d6
zF=e`74iB9$Wx7BQ4?SYabm1Hxdcc(Ff;l{N*p%r)IXrZ`DbodVc<6JcOc%!Cp`a<#
z1#x)jI#Z?#;qcH(Q>F{x@KA#((}iz%XqG9{L}7TS)RgH0I6S18GF|wFhu-~EwU;h<
zXum1bg%0gEWxBwj{iaM8HniWA>4Jv#D;d%4;?ZqlJid_csiA0(%KbLE*Cy|^$tj!M
zWs?n?e3MOXvB~Rgvd<<jx5<laa*a)%W0Nnk$&+nzkxkZZ@+X&%jn@ZOx#69)=~w>V
zpZ<-%=gkY7w)z?d8czHBmtTQlmYMu-(0sqWwq)i3bP9d|{mVZD;_ugoi7XvSPR38;
zG2-#laAnSw%-m0U&r1sZ(_DYib402?^`|fRPyFF(|A~<tzxI;<l~0r9aL_9qI5X$k
zdA}-q`u*%*PT4GVS&P4Cc`LF0^jpb7f8X+U2!lWWB$F8oVKBa=-vn8^3ASeWWIwbE
znZ&ff&VT#)0q)_rAE-b5=r&I7*}+MFCrER;x0{px-5>`BlHQVP-~N*7^@mETTaK1g
z-*l{`+88LQ?mAOaojO-iz4yYi^a~%{fdOk4({tXN-g~}h?|HUw3WD%7eq)0hQB%5c
zWJCAj(C0hfGobnJ^QTV^zJ(lr+Bf3w**n5gWia>-8O%@yZ--C%-Y3hqoBCSb_qRRg
z4-a&-x3~LG40=zDIMT13_;hCVh4hK^sT1cM+9_=y{Yo{2et&(R59(+9eekWFN&onu
zr^olc25_10{1<+RDvV~ngt$WO^z&vLZa+`=F8-dvOBlk|h|`cief-q#GntUTFL%y7
zREO%^FyQa&CA5#Fd(VQN?EMvvR9pYa-V6U}vLlM^zlU9rk$B=$gwCIS{XPG8{ppVm
zI{ZD)X-|^rGZfNi=}bB3M-<ZBXU}o&c~4~@FDU&I0`(xyW<02am56`)$zIRfnT&%y
zduS$1i~bBK-8%`8zn|y$dj@j->F50Yi_$O@1+0Nq@4&vfeY_BKM;-u65n;24$EcsA
zqx1=(|0JF6{OOlr7v+wUqmGV$JjT&7(@_^3JqtTF_*vMbd(R<aJ;&*oNm*NWxoNQs
z7AJdi(GrS`tZJ{5x2&JwzI!M*ecTm~9xAjR<@bz?b@?GK2O?mDC>p<HdZz17_j=*u
z*$`Ff3TkD#cOC)s)O2vJ0O~n_<_=C4z!Cr_d(X(;9(n^c>*FFub>D#($s_y>A|FG9
z6Vz$@M;yJt(R&1Wlrlw|o}j2et(DZ4_ELV*8j*-u{c2wQM;lR!X!R3_Ugk+^0_-1z
zeY*EenD-o~<Jcf9H@*B8Av)RnDm19zp<Lbu66u$bKI7L0@Wb)rXXV!h{WVYH+SJFE
z@_|y)dj=L54m8G})DV|v$OZkLAN>&a{r!|72MJ}1L2{b%(!C>8M9*;|Y~Om>pC<?V
zC|AO%>6StGFs|yN+Zs^gx3^iczHeqtp{(WpJ`q+?0W<V3Q_Y?lp|L(Dq6ba$*AcTG
z9t*9HlVv=BC3pTFKdm0#vs%w_>hGMAuS^1FR{K@B72~-84KU9jJ?Fb^=ZcfQyD3O-
z<5Y9EzrW=uM#_nEIsX2u9R7YF=@b6`$)86ncqj+of!nn2NYCCQELnsfdpW8)I0*0%
z%yITi`|c)Y#C6-TlfL`7<4gN|_tEfcdhsY-lBSM|X79WX2R**~w4S~9xo$hdgX-^F
zKN9j^R>;q4Nqz8zKV>qfF~y^n@cExP<D)_*(nJI%A}|qwi3t3Ej{vO-P%4O~x>}96
z7i%NoXonZ;BOS?3Z#cRW%Qhi<u2{11(n^Ryvk|1WNZ5!bubdO|ibWQ$Sg4UY1*~EI
z6}9#FYp`fa@7`c26gLuy0!x-$01C&VrU!dDa^Xj;(3o1I^)MHP$Um=+snls;$;rDb
z7LSB#Errs8kf~#LM2kaarnS8ZBfb-X3xyN1(niSI5?bRL*N(3<UFk*3yRigictbHG
z;f=<U-mYM>t<#IjfJIn>9Y%q<IwpfV8hLGc&a!5N2J;5C;}4n4e$WBT%DxSH26O<_
z`)(%F3F;coWX^(igVN2`!uK+na!dzzVlGfmlWrtT|9H}F-ORMz9&LJIzVi_B2&cUm
z$8cjamo%F4G2vs_0|GzzKl@H5vx9sTd#)+2xVFT-%ekLjJ?+X%7hEzMD7U{6)Pp*4
zLjsY=Zxa7b;G1L}2(Q5Zo%pXt-8%^I6??wuSY1SqhNvFYz9jNq05$U*PocKL_7va>
zv~yLiB_Ax=zKQ=8f6Qb^XVmstZcETanux$e1STRd5rK&aOhjNJ0uvGV+lzqOucY=T
zsi`ULnW9uEh`E8PNXc>&k=j$D_CZaT`Iw&ar1l!A{ZKS7rKIvd_%stE`Iy|?r1li;
zpb0RNB<!rr$0Kf@Zju#MdwyshP06}fhGc2RDy}AJbdS@Bw5NGFCABw6?Ww7twN?wh
zO;%Xddw}QSUPDq<{tPKAJv;&B?L%=K!n0N`kH`Y?JX_^+OWbyF`5Bpyhp=2$?U3&O
z&xqP@ZFg8NZ(bTD-6ZJ_NxLQ8FX<skk4kz>(g8`&NP14v3z9OqdCDWHSJHY(8ztQ&
z=?+P|CAHW4f0CVzLSxP9)mM7wwX~+9$&|ODzIIV<eN97(iw(EUpI=+Quv)_Ga#)Cm
z2clC?%l%u6)Y!C@IUQ<rH-;Y92GH+?m*P+dWIRKMGye#jJ96_M0LgJWG-n&hj{LWY
zXn6)m?!EAkGj}TODRemW7eR7qaV=+FRTGGwtB0dWXc2Vj6uM1yIQ2&`WO6s+kW(`q
ztH><3jhs9;{Q&qp=XXd+%ZtE!KJj_ZW29q1CwF}rd4h}Y!5S63m#T084bZ1^PtE}F
z{9G!hpdzmpCa!77)m;0*I$T{;rt5QL<SGEKyB<Prp6kot3koW6q~C)3XHWpmrG=Wj
zD&CO7xlr`v|IpbAtmsu}=0B#Be^&Hcs_2gjI;aGee<esUReKkh5^7x$T0p3WkkVh1
z=^fzmoOJZuN#HR!&!<S{IqB$mfWS}5@Ao)JN6%vft|Idj9HgV?pK&PsI@QhjfwKY@
zMHi^<f0A|kCe)oHCGUbbipZ<;!}8Z4<Q9Dey3RkB?S!e5<-f)~($RAfGA4Z=hbm{T
z=G+UW>;}@!(}d4554p+LgwwJIsW_)r`~&h@Mm^<pX_Nns+?M@@qymvw!Sd(9O`emE
zo;z?Ty%yQ|{T!sDhdQ(LT6oWYkb`vee2+jHUHL!aARRp?2wXv$Kj9!9J+BeiN8lSA
zq@#z1YUx)|xBNeHkd7WY_2%)O^td_uC0Kc;aP|nXvpD+*v6pl9C1S7OECty^-H^wF
z<5|zyy9nLJ*>4fs#@VNdjdJ!CV)t<Ncf=mx?8n6Z1K8ZJp#)b&*=Dpf_h}qlmE{KU
zUn726`EKH`gQ9DCc^~l&;GHujQP>L~gl(>Krjxq4=p<Y^XO;XIJWwLe&rf;?q}=2D
zcl|PS%;Z~1<v$8Ie*<it=iJK(|2-+c;a&%x6@MK9p?nONuwrsSIb6<Xyu+!Rxtzla
zAAym_`Aa8_sG@1)^*63hU;|gweP2|n`OMh`Ui?Vi7w6^Ry6XBDXlSlp)XU-8fMc$!
z3mzI>x52@FH@`A#Zhp0KxVMwfT-OO?Ib9!v_qh3#z+3PzY)CHn3JO5-7PNB88Vh#v
zny~$B%3QK@H<!>}gQ?xgICF1sErZ(B_7HP#%>Nv*sRi?yds86=cWP?!Lgv1%@ELND
zTY`goa}L$H@F~=@(7n}}gi!P(vfbBn3syuO?7qQ6CFHV-yI|umbaK2{&Ph&zr=%VE
z7&?`+QEx{_3Gq3H5AB;4#~yk|r;99d4&S+N`gPKbw;bRvpg1ke95)vWl>_@&)x%O}
zDfvaB{J6|NxDTE1Gk|Vf7kGcU*hPD&(9(Qr`ulRK!JpJY){X>mjh_*WcR-_@G;$6<
z-cq>$jH66e>r_kSGl;UIT*^Obsl1is$rW@K$vM3L`pRX9iDOCu`N}zbE-=m5$sAKF
znt3bVZ>gk^Ii^;{dG@jED|5)aqVx_fAG*G>l;q2#`!g+7v*iTn6Vc1Og;$k`2n@7T
zHOTB=sO+u;s=s{lqrCcOTX=jND@vc@^0}7E&r<omBH`fGma5-L2XmBzD05spNo2o(
z;^jQ*4D2aY&cng6p;Gu8Y~dh3^HYmv(wQG;nqq1$HHnA4jTKXM?ox6NyXf<fwb}XK
z1gV$Z@B(;B+L2)>dY?djwAr(%B96m1R??MTt9Fxt<8WtHS{jM+dB1CQyx$#%LyXP(
zBU-6d<*5sN2h1pGN4zg%lxkHoMSLE=Z8oG*ZBB8SJEwShaj7;b--CcSE@5u>3P9+?
z60jK2Q&>*%mF{KkrS7$uZs<}0)yg9U!t&=bBDj9!6t8d-CiFFcu6Z!8f}aA;U_iP#
zgD@4ALEr5j)h#Nr6tx64#hSLZjJ%Z0LJ4(1JXi3dO3R>m6}otm=|f-YCe1SG1LE2;
z@=-ojvz9U0S_WfNrrPQ-75UE5=2p~dS67a<pTg8w@zqt8R8-|cOLZL>F4F**wggOf
zBgB*PNx4d}j9p~)_?gPJWn+BKz?&pvT+HMxN3^`iG5dj>&l>B<bc45o_|W|+`a6eB
zq0yv$TwH{sMkAf%T2_}z#OuP*wn!>u)ZtCLz~Y7SwY!dJs;)JR7kR8IV}v@q2}ExL
zjRpV0Nn%d|JM5C-NTRlrN;TR#V*#<o6X;4slHuAm8kxyG-3Do(wuxjY90Q9XEMHo(
zX5uv@TDFp^{G}wphohi4d60QDS(Czn5OT=10^~&ie1!@u;>mQ4xg6?ul2fJWy5pvD
zU3*Y>zNNc5bk9@zr4Q)S+jZybx+_Iu@h)9Y>N7Ay&Z*NYi8wBuq?Zu0O~|M8GLmZb
z*+gn|yb6@7U8}plsF$D83s>mcx?D%jjkS*1lXTZxZ|Ma`^-{;;DZ0~nNOv99r#TNC
z)+_GUT|0rirn}aib{<fiUhe#o?tG8bN}RpA=Nr1~DUr8bp8}xbb$!bHdPQpVSh7pc
zyI-HYU7zmk&`aOci&A=_6XY;=Rp_{+jQB~gb=|Me+^&0^!+Pn{`dnv^UhcTKT%Y_6
zz2dZ9`3>h$=Yx6$EGyUP7o$CnoNnhGx{F$t)aP&@?)2(;0?>2joH~slKB*TEQncpQ
z=_Oa`g^p^J*72178Pc!Pr;_p^5uU5`;vJ+}UZ)q{uNQ3>I?j*^USUMnwmYU^_~HZ~
zpts1deuv>B-xmu7lfgie`8I8AZoXmjhK-xo^LN|=t$UJ2g2`9ku%1dofrd{YoQPS3
zXfWO_jMr~jBbbdFd|Uh*w<?#bR&How9Y)lM<3+ZBShK~8Vzg$97wPbRTp&OrZoz!i
zA`y-*4zx#NyTZ|SltL1fDODO4lS*eSp2RAztn%6oqMXf5{+3O`0*jj6Mu4guj^fp~
zu5e_J(BT?*v5!Nlv~IYAe<~^^n~Rt0#`&q40l>?5CLiwJO_dYXY*-9Fk-$5HDxjO2
zctFLEmTlR%dR?Gt&E}QsM;)Y8`<I9ciT3i|@NHY&+_Giu^*$z+w&?|1@9Kst<jt(N
zrYo6>8q0ZT!)*|Q@wU$8;;mnAO^3IpJ($=NZR?E3qOnw>CKW|gLp4cyUoU}7{(4|d
zcRZHFYm2dX4Z0+TWptI%76~RgRW>bZ*R;2_TV&bSEn8)$i8!aCiEu~Mz><1rFfJOr
zxoQ1qh?WSmO`F&9-nrF?#~9us#OggAOl#`i(HKV;%du+g#0oP@h#0lU;#hVTWo+2E
zVO2At%M!r=4QM$^#`hPB2%{~&>o%=iz0S8)h^w17`nLI2TYeUJWs?_$o@q_Bw;S=%
zIvIG4Q;gfi3y~dd4<)GYM^Bfs?JI8(4MrinPd7L5p93EWvT(DVyXP<BLE$5EG$<4c
z-tdHUwRtmDjI?OTsPQ~nHw48Tl9-ow2BV>f5fJZ);x~=AQpFOzdWRJAcwV0^TQ~bG
zLzC5Y#=4BUNPYd{h4YszTv8W{+_I>lzAhPyMG|%72*2REgb`^cK2f*34R6H;y5mN>
zfz1G-t^wcmz71Qq;JwxOPSI=XWm<K{S-H+fzgzR<5*2l6TD{tW26<?pL)W{ki7w8$
z>|MTR)T%_!^)%$%iOV7-FF#_Y13I2QJ@<29D(WExOY(vzywQd)khps8s!Y~`(eUQG
zr%@nOKT0(7)YufJo}+RQhcDot{14dtALROz&G%E4{<px4SAQ}e52uN@hTRLEi<O5_
zI-dS%u0NI8pDX?=H!EWUvqtTSdkg;>c0s7;vnuyvFq4#Eq4b{qc=+YOy%s&i>X?vt
z8Ro2w3K7(w#m{!&9_Xw1E2|x(?<mD|Qz3%3XYoTfe&f~i?kxC2S@0ia!GD|u|5X-z
zI18SG_)vT8@$vAujcqGQGl7>nDw%qYsx&SqoLcM^2xf*nEdxGY9InfP-<$=fM`RuZ
zzoJn(mc+&NJ?3{@=1QENeUZMsAL)tQB;?p168s~_$NDDe;Vk$I(vSUi@|Rik{~-14
zPXV@P(f=R|?!x(q;%{%)WZ>SvtUjv)K3@FSWWnjt?(A{=+)DZmtOj|;X71;HJm#})
zb?e)o#XsB97Ed<R#&ER>wuS@AU<U(`Na9k7JCo551q6~^xNV6=F`WzqLa{(cB-R>?
z1VWf};8LF2jZY_Zbw`Y(5vr{pUxU8I5Do<6@!*~SW&rU$tUVs=G6JDgSJxhxSR_p3
z!pSkJ<IUv)fom`e^96hxngaM*LDTjPE7z}G4NsV21$=&)>u=i30&AK#u3Fg~*m%t~
zTYOssTUV}X_QAsbk%vb0^#pt;0TU_unu7_^XB7UsZzs@KAI2&hd=A4~P2Mp58=G(Y
zw1b(sP<$K0l&NIeAvgvQ-<%i&p?4C-7{uI(XrlCq2-+BE0=!v#wk1CMfS`>121Fo~
zhy}##1MS}kE!<uPQVHz8G{1#0#+#ZO(1$k0WQfh1V<3!!5K3%KBxLFQRzg&p_C1=)
zRBz>2Y(*T+TA1y=M0`=hRHUZLIlSC3IVLL*z_&2eSfY<)jQiyaRSEi9h-r@DB10`U
zg_;>l(Kzc@NX!f#b{gbkCw5}$IYx`sCiZk;x(}L+3)-nTOeziBxYc6wrcsM07PYt!
zK%<j;EKqAIjBg3yK1yh<T(uTA4;_rFcjD%N)rR&&QG}p)FwAqXu{43(g)u?^d2u5W
zBm>EH;|8>rH=`D8ZAT0uF$trM)h3PI5a~w`?HK<t)Eb@gXWohXK~oYw#P3~LC>}-O
zmITg(IR*-=#fhei?jUMIMr*1An!#uX&Pr0`_na!LwKa~%uTsMIlnliqf@4pcf3-9n
zmbjE-&L>g@&a;#hRqN{l6}pNidNKa-ywkK_BJCBeCnZaw2cgzP-`%m=+rOiAiB%cA
zng=MD=C@XRHNRHWs}v=*+ur~jTUukP{c7KUqN;qgHfyhc5cYUJX}15YY`>xhrDLii
z#aY?&hkKC2_$L!pzS^gtsM?nR6`sh4HPHiRTJu--YMozEiZ3Nqf2F7BZOEfN1WH!>
z2o&8x#z^-13zV^bFcec|ul6-4Iv^EQ`S$kTEA3ZFJ+)6lQMIpv>?vk;`v-tgOq73f
zJ|X87G`_9L-v39CX|?xCJ4JWc0<NYjihj#xzaLY2N{T8yfsRgh6n+dD*76TYdqu5#
zyUB;LQ}hX&{W&=~Q*;J(oFyqc1<|{C*7D8!5qV!yq!cB!$L~dHulk>PrJ|(AZShn6
zC$-p5!H|lwSNC&@HY-I*?dAW{X78CT6cwf20M=wLpY9jwvjy^JYu?|g@7~$<m8|Hy
zu%-1yWv}k1y?iNYbi6`^CuOJTCqQZJDtmRGtG;7+LDpY&qq0-|_+8kNin3Sty%lu;
zkp=}ZkIG)rJQ!Q;S)){~7gUv_94I>_7s8k@Rld59@JM^RK9yxnrLugfZe}U_%S=Qt
z%3e`gvsS!Sb(wT%_iNX!&SHOyG@NN`fZcvk7W*$R5w-cX&A=uy`o5eUBg@V!g+1*+
zuqM0iMrm(v=Tcg}M|$4&gHiTYYAZq*t`g)REwUiZ#y_1}R5;Z2%LPpJJy<gxTqD?{
awoFy7Ql!(JO@(L8TPHL=YcsHk?7sjWb7VRI
literal 0
HcmV?d00001
diff --git a/tools/testing/selftests/xcall_prefetch/echo_server.c b/tools/testing/selftests/xcall_prefetch/echo_server.c
new file mode 100644
index 000000000000..49c1ea035788
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/echo_server.c
@@ -0,0 +1,110 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/epoll.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "./utils.h"
+
+static int client_id_counter = 0;
+char buffers[NUM_THREADS][BUFFER_SIZE];
+
+int main() {
+ int server_fd, epoll_fd;
+ struct sockaddr_in address;
+ struct epoll_event ev, events[MAX_EVENTS];
+ struct client_info *clients[MAX_EVENTS] = {0};
+
+ if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
+ handle_error("socket failed\n");
+ }
+
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = INADDR_ANY;
+ address.sin_port = htons(PORT);
+
+ if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
+ handle_error("bind failed\n");
+ }
+
+ if (listen(server_fd, 100) < 0) {
+ handle_error("listen failed\n");
+ }
+
+ debug_printf("Server listening on port %d...\n", PORT);
+
+ epoll_fd = epoll_create1(0);
+ if (epoll_fd == -1) {
+ handle_error("epoll_create1\n");
+ }
+
+ ev.events = EPOLLIN;
+ ev.data.fd = server_fd;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) == -1) {
+ handle_error("epoll_ctl: server_fd\n");
+ }
+
+ while (1) {
+ int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
+ if (nfds == -1) {
+ handle_error("epoll_wait failed\n");
+ }
+
+ for (int n = 0; n < nfds; ++n) {
+ if (events[n].data.fd == server_fd) {
+ int client_fd;
+ socklen_t addr_len = sizeof(address);
+ if ((client_fd = accept(server_fd, (struct sockaddr *)&address, &addr_len)) < 0) {
+ debug_printf("accept failed\n");
+ goto fail;
+ }
+
+ struct client_info *info = malloc(sizeof(struct client_info));
+ info->fd = client_fd;
+ info->id = client_id_counter++;
+ info->addr = address;
+
+ clients[client_fd] = info;
+ debug_printf("new connection accepted client:%d\n", info->id);
+
+ int flags = fcntl(client_fd, F_GETFL, 0);
+ fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
+
+ ev.events = EPOLLIN | EPOLLET;
+ ev.data.fd = client_fd;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) == -1) {
+ debug_printf("epoll_ctl: client_fd\n");
+ goto fail;
+ }
+ } else {
+ int client_fd = events[n].data.fd;
+ // char buffer[BUFFER_SIZE] = {0};
+ struct client_info *info = clients[client_fd];
+ char *buffer = buffers[info->id];
+ memset(buffers[info->id], 0, BUFFER_SIZE);
+ ssize_t read_bytes = read(client_fd, buffer, BUFFER_SIZE);
+
+ if (read_bytes <= 0) {
+ if (read_bytes == 0) {
+ debug_printf("client:%d disconnected\n", info->id);
+ continue;
+ } else {
+ debug_printf("read failed\n");
+ }
+ goto fail;
+ } else {
+ debug_printf("Server received client:%d %s\n", info->id, buffer);
+ write(client_fd, buffer, read_bytes);
+ }
+ }
+ }
+ }
+
+fail:
+ close(server_fd);
+ return -1;
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/echo_test.c b/tools/testing/selftests/xcall_prefetch/echo_test.c
new file mode 100644
index 000000000000..fc7180869f3c
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/echo_test.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "./utils.h"
+
+int main() {
+ pid_t server_pid, client_pid;
+ int server_status, client_status;
+
+ server_pid = fork();
+ if (server_pid == 0) {
+ execl("./echo_server", "./echo_server", NULL);
+ printf("execl echo_server failed\n");
+ exit(EXIT_FAILURE);
+ } else if (server_pid < 0) {
+ printf("fork echo_server failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ sleep(1);
+
+ client_pid = fork();
+ if (client_pid == 0) {
+ execl("./echo_client", "./echo_client", NULL);
+ printf("execl client failed\n");
+ exit(EXIT_FAILURE);
+ } else if (client_pid < 0) {
+ printf("fork echo_client failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ waitpid(client_pid, &client_status, 0);
+
+ kill(server_pid, SIGTERM);
+ waitpid(server_pid, &server_status, 0);
+
+ if (WIFEXITED(client_status)) {
+ int exit_code = WEXITSTATUS(client_status);
+ if (exit_code == 0) {
+ printf("echo test %sok%s.\n", ANSI_COLOR_GREEN, ANSI_COLOR_RESET);
+ exit(EXIT_SUCCESS);
+ } else {
+ printf("echo test %sfailed%s.\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ printf("echo test %sfailed%s.\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
+ exit(EXIT_FAILURE);
+ }
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/mulit_close_test.c b/tools/testing/selftests/xcall_prefetch/mulit_close_test.c
new file mode 100644
index 000000000000..fae0407e8473
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/mulit_close_test.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+
+#include "./utils.h"
+
+int main() {
+ int epoll_fd;
+ struct epoll_event ev, events[1];
+ int ret = -1;
+
+ int fds[2];
+ socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+
+ epoll_fd = epoll_create1(0);
+ ev.events = EPOLLIN | EPOLLHUP;
+ ev.data.fd = fds[0];
+ epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fds[0], &ev);
+
+ if (fork() == 0) {
+ close(fds[1]);
+ exit(0);
+ }
+
+ close(fds[1]);
+
+ int nfds = epoll_wait(epoll_fd, events, 1, 1000);
+
+ if (nfds == 1) {
+ if (events[0].events & EPOLLHUP) {
+ ret = 0;
+ }
+ }
+
+ close(fds[0]);
+ close(epoll_fd);
+ if (!ret) {
+ debug_printf("epoll_wait detected EPOLLHUP!\n");
+ printf("multi close test %sok%s.\n", ANSI_COLOR_GREEN, ANSI_COLOR_RESET);
+ return 0;
+ }
+
+ printf("multi close test %sfailed%s.\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
+
+ return -1;
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/multi_write_test.c b/tools/testing/selftests/xcall_prefetch/multi_write_test.c
new file mode 100644
index 000000000000..0af07b742e73
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/multi_write_test.c
@@ -0,0 +1,77 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/wait.h>
+
+#include "./utils.h"
+
+#define NUM_CHILDREN 3
+
+int main() {
+ int epoll_fd, pipe_fd[2];
+ struct epoll_event ev, events[MAX_EVENTS];
+ char buffer[BUFFER_SIZE];
+
+ if (pipe(pipe_fd) == -1) {
+ handle_error("pipe failed\n");
+ }
+
+ epoll_fd = epoll_create1(0);
+ if (epoll_fd == -1) {
+ handle_error("epoll_create1 failed\n");
+ }
+
+ ev.events = EPOLLIN;
+ ev.data.fd = pipe_fd[0];
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipe_fd[0], &ev) == -1) {
+ handle_error("epoll_ctl failed\n");
+ }
+
+ for (int i = 0; i < NUM_CHILDREN; i++) {
+ sleep(1);
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(pipe_fd[0]);
+ char msg[10];
+ sprintf(msg, "%d %d\n", 2*i, 2*i+1);
+ write(pipe_fd[1], msg, strlen(msg));
+ exit(0);
+ } else if (pid < 0) {
+ handle_error("fork failed\n");
+ }
+ }
+
+ sleep(1);
+
+ int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
+ if (nfds == -1) {
+ handle_error("epoll_wait failed\n");
+ }
+
+ debug_printf("epoll_wait returned %d events\n", nfds);
+
+ for (int i = 0; i < nfds; i++) {
+ if (events[i].data.fd == pipe_fd[0]) {
+ ssize_t n = read(pipe_fd[0], buffer, BUFFER_SIZE);
+ if (n > 0) {
+ debug_printf("Received: %.*s", (int)n, buffer);
+ }
+ char *expected_msg = "0 1\n2 3\n4 5\n";
+ if (strncmp(expected_msg, buffer, strlen(expected_msg)) != 0) {
+ printf("multi write test %sfailed%s.\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
+ handle_error("expected msg: %s, buffer: %s", expected_msg, buffer);
+ }
+ }
+ }
+
+ for (int i = 0; i < NUM_CHILDREN; i++) {
+ wait(NULL);
+ }
+
+ close(pipe_fd[0]);
+ close(pipe_fd[1]);
+ close(epoll_fd);
+ printf("multi write test %sok%s.\n", ANSI_COLOR_GREEN, ANSI_COLOR_RESET);
+ return 0;
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/signal_recovery_test.c b/tools/testing/selftests/xcall_prefetch/signal_recovery_test.c
new file mode 100644
index 000000000000..9645f8ffec1b
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/signal_recovery_test.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "./utils.h"
+
+
+int epoll_fd;
+int server_fd;
+volatile sig_atomic_t signal_received = 0;
+
+void signal_handler() {
+ signal_received = 1;
+}
+
+void* worker_thread() {
+ struct epoll_event events[MAX_EVENTS];
+
+ while (!signal_received) {
+ int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
+ if (nfds == -1) {
+ if (errno == EINTR) {
+ debug_printf("epoll_wait interrupted by signal! re-waiting...\n");
+ continue;
+ } else {
+ handle_error("epoll_wait failed\n");
+ }
+ }
+
+ for (int i = 0; i < nfds; i++) {
+ if (events[i].data.fd == server_fd) {
+ struct sockaddr_in address;
+ socklen_t addr_len = sizeof(address);
+ int client_fd = accept(server_fd, (struct sockaddr*)&address, &addr_len);
+ if (client_fd < 0) {
+ handle_error("accept failed\n");
+ }
+
+ struct epoll_event ev;
+ ev.events = EPOLLIN | EPOLLET;
+ ev.data.fd = client_fd;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) < 0) {
+ close(client_fd);
+ handle_error("epoll_ctl failed\n");
+ }
+ } else {
+ char buffer[1024];
+ ssize_t n = read(events[i].data.fd, buffer, sizeof(buffer));
+ if (n <= 0) {
+ if (n < 0) {
+ handle_error("read failed\n");
+ }
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
+ close(events[i].data.fd);
+ debug_printf("closing fd %d\n", events[i].data.fd);
+ } else {
+ buffer[n] = '\0';
+ debug_printf("receive: %s\n", buffer);
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void* client_thread() {
+ sleep(1);
+
+ int client_fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in server_addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(8082),
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+
+ if (connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
+ debug_printf("connect failed\n");
+ goto fail;
+ }
+
+ const char *msg = "hello world.";
+ write(client_fd, msg, strlen(msg));
+ close(client_fd);
+
+ sleep(1);
+
+ debug_printf("Sending SIGUSR1 to interrupt epoll_wait...\n");
+ kill(getpid(), SIGUSR1);
+
+ sleep(1);
+ debug_printf("Testing if epoll_wait can still work after signal...\n");
+
+ client_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
+ debug_printf("connect failed\n");
+ goto fail;
+ }
+
+ const char *msg2 = "hello world again.";
+ if (write(client_fd, msg2, strlen(msg2)) < 0) {
+ goto fail;
+ }
+
+ close(client_fd);
+ return NULL;
+
+fail:
+ close(client_fd);
+ pthread_exit((void *)42);
+}
+
+int main() {
+ pthread_t worker, client;
+
+ signal(SIGUSR1, signal_handler);
+
+ server_fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(8082),
+ .sin_addr.s_addr = INADDR_ANY,
+ };
+ bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
+ listen(server_fd, SOMAXCONN);
+
+ epoll_fd = epoll_create1(0);
+ if (epoll_fd < 0) {
+ handle_error("epoll_create1 failed\n");
+ }
+
+ struct epoll_event ev;
+ ev.events = EPOLLIN | EPOLLET;
+ ev.data.fd = server_fd;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) < 0) {
+ handle_error("epoll_ctl failed\n");
+ }
+
+ pthread_create(&worker, NULL, worker_thread, NULL);
+ pthread_create(&client, NULL, client_thread, NULL);
+
+ void *retval;
+ pthread_join(worker, NULL);
+ pthread_join(client, &retval);
+
+ close(server_fd);
+ close(epoll_fd);
+
+ if (retval != NULL) {
+ printf("signal recovery test %sfailed%s.\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
+ return -1;
+ }
+
+ printf("signal recovery test %sok%s.\n", ANSI_COLOR_GREEN, ANSI_COLOR_RESET);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/thundering_herd_test.c b/tools/testing/selftests/xcall_prefetch/thundering_herd_test.c
new file mode 100644
index 000000000000..bf98fcf94b6a
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/thundering_herd_test.c
@@ -0,0 +1,119 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "./utils.h"
+
+#define TIMEOUT_MS 1000
+
+int create_client_connection() {
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(8081),
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+ return fd;
+}
+
+void set_nonblocking(int fd) {
+ int flags = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+}
+
+int main() {
+ int listen_fd, epoll_fd;
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(8081),
+ .sin_addr.s_addr = INADDR_ANY,
+ };
+
+ int shm_id = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | 0666);
+ int tmp_cnt;
+ int *accept_count = (int *)shmat(shm_id, NULL, 0);
+ *accept_count = 0;
+
+ listen_fd = socket(AF_INET, SOCK_STREAM, 0);
+ set_nonblocking(listen_fd);
+ int reuse = 1;
+ setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
+ bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr));
+ listen(listen_fd, SOMAXCONN);
+
+ for (int i = 0; i < NUM_THREADS; i++) {
+ if (fork() == 0) {
+ usleep(1000 * i);
+ epoll_fd = epoll_create1(0);
+ struct epoll_event ev = {
+ .events = EPOLLIN | EPOLLET,
+ .data.fd = listen_fd,
+ };
+ epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev);
+
+ debug_printf("Worker %d waiting...\n", getpid());
+
+ struct epoll_event events[MAX_EVENTS];
+ int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, TIMEOUT_MS);
+
+ if (nfds == -1) {
+ handle_error("epoll_wait failed\n");
+ }
+
+ debug_printf("Worker %d: epoll_wait returned %d events\n", getpid(), nfds);
+
+ int client_fd = accept(listen_fd, NULL, NULL);
+ if (client_fd >= 0) {
+ debug_printf("Worker %d accepted a connection!\n", getpid());
+ __sync_fetch_and_add(accept_count, 1);
+ close(client_fd);
+ } else if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ debug_printf("accept failed\n");
+ }
+
+ close(epoll_fd);
+ exit(0);
+ }
+ }
+
+ sleep(1);
+
+ debug_printf("Parent creating client connection...\n");
+ int client_fd = create_client_connection();
+ sleep(1);
+ close(client_fd);
+
+ int waited = 0;
+ for (int i = 0; i < NUM_THREADS; i++) {
+ if (wait(NULL) < 0 && errno == ECHILD) {
+ break;
+ }
+ waited++;
+ }
+
+ tmp_cnt = *accept_count;
+ debug_printf("Total successful accepts: %d\n", tmp_cnt);
+
+ shmdt(accept_count);
+ shmctl(shm_id, IPC_RMID, NULL);
+ close(listen_fd);
+
+ if (tmp_cnt == 1) {
+ printf("thundering herd test %sok%s.\n", ANSI_COLOR_GREEN, ANSI_COLOR_RESET);
+ return 0;
+ } else {
+ printf("thundering herd test %sfailed%s.\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
+ return -1;
+ }
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/utils.h b/tools/testing/selftests/xcall_prefetch/utils.h
new file mode 100644
index 000000000000..58959c564119
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/utils.h
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#define ANSI_COLOR_GREEN "\x1b[32m"
+#define ANSI_COLOR_RED "\x1b[31m"
+#define ANSI_COLOR_RESET "\x1b[0m"
+
+#define PORT 8080
+#define BUFFER_SIZE 4097
+#define MAX_EVENTS 100
+#define MAX_MSGS 4
+#define NUM_THREADS 4
+
+#ifdef DEBUG
+#define debug_printf(fmt, ...) (printf(fmt, ##__VA_ARGS__))
+#else
+#define debug_printf(fmt, ...) (void)fmt
+#endif
+
+struct client_info {
+ int fd;
+ int id;
+ struct sockaddr_in addr;
+};
+
+void handle_error(const char *format, ...)
+{
+ printf("%s", format);
+ exit(EXIT_FAILURE);
+}
+
+char* generate_number_string(int length)
+{
+ if (length <= 0) {
+ printf("\nnumber string length invalid\n");
+ return NULL;
+ }
+
+ char* result = (char*)malloc((length + 1) * sizeof(char));
+ if (result == NULL) {
+ printf("\nnumber string malloc failed\n");
+ return NULL;
+ }
+
+ for (int i = 0; i < length; i++) {
+ result[i] = '0' + (i % 10);
+ }
+
+ result[length] = '\0';
+
+ return result;
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/xcall_prefetch_test.sh b/tools/testing/selftests/xcall_prefetch/xcall_prefetch_test.sh
new file mode 100755
index 000000000000..e9a990dac69a
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/xcall_prefetch_test.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+RED="\033[31m"
+GREEN="\033[32m"
+RESET="\033[0m"
+
+make clean
+make
+
+if [ -e "/proc/$$/xcall" ]; then
+ echo 22 > "/proc/$$/xcall"
+ echo @22 > "/proc/$$/xcall"
+ echo "enable xcall epoll prefetch, pid is $$."
+else
+ echo "Warnning: not enable xcall."
+fi
+
+./echo_test
+sleep 1
+
+./multi_write_test
+sleep 1
+
+./thundering_herd_test
+sleep 1
+
+./mulit_close_test
+sleep 1
+
+./signal_recovery_test
+sleep 1
+
+if [ $? -eq 0 ]; then
+ echo -e "${GREEN}tests passed successfully.${RESET}"
+ exit 0
+else
+ echo -e "${RED}tests failed.${RESET}"
+ exit 1
+fi
\ No newline at end of file
--
2.34.1
2
1
---
.../testing/selftests/xcall_prefetch/Makefile | 37 ++++
.../selftests/xcall_prefetch/echo_client.c | 119 +++++++++++++
.../xcall_prefetch/echo_client_multi | Bin 0 -> 21896 bytes
.../selftests/xcall_prefetch/echo_server.c | 110 ++++++++++++
.../selftests/xcall_prefetch/echo_test.c | 52 ++++++
.../xcall_prefetch/mulit_close_test.c | 48 +++++
.../xcall_prefetch/multi_write_test.c | 77 ++++++++
.../xcall_prefetch/signal_recovery_test.c | 164 ++++++++++++++++++
.../xcall_prefetch/thundering_herd_test | Bin 0 -> 21000 bytes
.../xcall_prefetch/thundering_herd_test.c | 119 +++++++++++++
.../testing/selftests/xcall_prefetch/utils.h | 54 ++++++
.../xcall_prefetch/xcall_prefetch_test.sh | 39 +++++
12 files changed, 819 insertions(+)
create mode 100644 tools/testing/selftests/xcall_prefetch/Makefile
create mode 100644 tools/testing/selftests/xcall_prefetch/echo_client.c
create mode 100755 tools/testing/selftests/xcall_prefetch/echo_client_multi
create mode 100644 tools/testing/selftests/xcall_prefetch/echo_server.c
create mode 100644 tools/testing/selftests/xcall_prefetch/echo_test.c
create mode 100644 tools/testing/selftests/xcall_prefetch/mulit_close_test.c
create mode 100644 tools/testing/selftests/xcall_prefetch/multi_write_test.c
create mode 100644 tools/testing/selftests/xcall_prefetch/signal_recovery_test.c
create mode 100755 tools/testing/selftests/xcall_prefetch/thundering_herd_test
create mode 100644 tools/testing/selftests/xcall_prefetch/thundering_herd_test.c
create mode 100644 tools/testing/selftests/xcall_prefetch/utils.h
create mode 100755 tools/testing/selftests/xcall_prefetch/xcall_prefetch_test.sh
diff --git a/tools/testing/selftests/xcall_prefetch/Makefile b/tools/testing/selftests/xcall_prefetch/Makefile
new file mode 100644
index 000000000000..5bd509df66e0
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/Makefile
@@ -0,0 +1,37 @@
+CFLAGS += -Wall -Wextra -g
+TEST_GEN_FILES := echo_server echo_client echo_test multi_write_test thundering_herd_test mulit_close_test signal_recovery_test
+
+# enable debug
+# echo_client: CFLAGS += -DDEBUG=1
+# echo_server: CFLAGS += -DDEBUG=1
+# multi_write_test: CFLAGS += -DDEBUG=1
+# thundering_herd_test: CFLAGS += -DDEBUG=1
+# mulit_close_test: CFLAGS += -DDEBUG=1
+# signal_recovery_test: CFLAGS += -DDEBUG=1
+
+all: $(TEST_GEN_FILES)
+
+echo_server: echo_server.c
+ $(CC) $(CFLAGS) $< -o $@
+
+echo_client: echo_client.c
+ $(CC) $(CFLAGS) $< -o $@
+
+echo_test: echo_test.c
+ $(CC) $(CFLAGS) $< -o $@
+
+multi_write_test: multi_write_test.c
+ $(CC) $(CFLAGS) $< -o $@
+
+thundering_herd_test: thundering_herd_test.c
+ $(CC) $(CFLAGS) $< -o $@
+
+mulit_close_test: mulit_close_test.c
+ $(CC) $(CFLAGS) $< -o $@
+
+signal_recovery_test: signal_recovery_test.c
+ $(CC) $(CFLAGS) $< -o $@
+
+.PHONY: all
+
+include ../lib.mk
diff --git a/tools/testing/selftests/xcall_prefetch/echo_client.c b/tools/testing/selftests/xcall_prefetch/echo_client.c
new file mode 100644
index 000000000000..2e3c41b2b14e
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/echo_client.c
@@ -0,0 +1,119 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include "./utils.h"
+
+#define DEBUG
+
+void *client(void *arg)
+{
+ int thread_id = *(int *)arg;
+ int sock = 0;
+ struct sockaddr_in serv_addr;
+ struct epoll_event ev, events[MAX_EVENTS];
+ const char* test_messages[MAX_MSGS];
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ handle_error("client:%d create socket failed\n", thread_id);
+ }
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_port = htons(PORT);
+
+ if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
+ close(sock);
+ handle_error("client:%d invalid address\n", thread_id);
+ }
+ if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {
+ close(sock);
+ handle_error("client:%d connection failed\n", thread_id);
+ }
+
+ debug_printf("Thread %d connected\n", thread_id);
+
+ int epoll_fd = epoll_create1(0);
+ if (epoll_fd == -1) {
+ handle_error("client:%d epoll_create1 failed\n", thread_id);
+ }
+
+ ev.events = EPOLLIN;
+ ev.data.fd = sock;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock, &ev) == -1) {
+ handle_error("client:%d epoll_ctl add sock failed\n", thread_id);
+ }
+
+ test_messages[0] = "hello world.";
+ test_messages[1] = generate_number_string(100);
+ test_messages[2] = generate_number_string(1024);
+ test_messages[3] = generate_number_string(4096);
+
+
+ for (int i = 0; i < MAX_MSGS; i++) {
+ const char* msg = test_messages[i];
+ ssize_t msg_len = strlen(msg);
+ if (send(sock, msg, msg_len, 0) != msg_len) {
+ handle_error("client:%d send failed\n", thread_id);
+ }
+
+ debug_printf("Client:%d send %s\n", thread_id, msg);
+
+ int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
+ if (nfds == -1) {
+ handle_error("client:%d epoll_wait failed\n", thread_id);
+ }
+
+ for (int n = 0; n < nfds; ++n) {
+ if (events[n].data.fd == sock) {
+ char buffer[BUFFER_SIZE] = {0};
+ ssize_t read_bytes = read(sock, buffer, BUFFER_SIZE);
+
+ if (read_bytes <= 0) {
+ close(sock);
+ if (read_bytes == 0) {
+ handle_error("client:%d server disconnected\n", thread_id);
+ } else {
+ handle_error("client:%d read failed\n", thread_id);
+ }
+ } else {
+ debug_printf("Client:%d received %s\n", thread_id, buffer);
+ if (strncmp(msg, buffer, msg_len) != 0) {
+ handle_error("client:%d error: response does not match sent message\n", thread_id);
+ }
+ }
+ }
+ }
+ }
+
+ close(sock);
+ pthread_exit(NULL);
+}
+
+int main()
+{
+ pthread_t threads[NUM_THREADS];
+ int ret;
+
+ for (int i = 0; i < NUM_THREADS; i++) {
+ ret = pthread_create(&threads[i], NULL, client, &i);
+ if (ret != 0) {
+ perror("pthread_create failed");
+ continue;
+ }
+ }
+
+ for (int i = 0; i < NUM_THREADS; i++) {
+ if (threads[i] != 0) {
+ pthread_join(threads[i], NULL);
+ }
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/echo_client_multi b/tools/testing/selftests/xcall_prefetch/echo_client_multi
new file mode 100755
index 0000000000000000000000000000000000000000..36aa08d6e12f6e8ec73b2a7f8eb567225eb0b2f8
GIT binary patch
literal 21896
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
awoFy7Ql!(JO@(L8TPHL=YcsHk?7sjWb7VRI
literal 0
HcmV?d00001
diff --git a/tools/testing/selftests/xcall_prefetch/echo_server.c b/tools/testing/selftests/xcall_prefetch/echo_server.c
new file mode 100644
index 000000000000..49c1ea035788
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/echo_server.c
@@ -0,0 +1,110 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/epoll.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "./utils.h"
+
+static int client_id_counter = 0;
+char buffers[NUM_THREADS][BUFFER_SIZE];
+
+int main() {
+ int server_fd, epoll_fd;
+ struct sockaddr_in address;
+ struct epoll_event ev, events[MAX_EVENTS];
+ struct client_info *clients[MAX_EVENTS] = {0};
+
+ if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
+ handle_error("socket failed\n");
+ }
+
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = INADDR_ANY;
+ address.sin_port = htons(PORT);
+
+ if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
+ handle_error("bind failed\n");
+ }
+
+ if (listen(server_fd, 100) < 0) {
+ handle_error("listen failed\n");
+ }
+
+ debug_printf("Server listening on port %d...\n", PORT);
+
+ epoll_fd = epoll_create1(0);
+ if (epoll_fd == -1) {
+ handle_error("epoll_create1\n");
+ }
+
+ ev.events = EPOLLIN;
+ ev.data.fd = server_fd;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) == -1) {
+ handle_error("epoll_ctl: server_fd\n");
+ }
+
+ while (1) {
+ int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
+ if (nfds == -1) {
+ handle_error("epoll_wait failed\n");
+ }
+
+ for (int n = 0; n < nfds; ++n) {
+ if (events[n].data.fd == server_fd) {
+ int client_fd;
+ socklen_t addr_len = sizeof(address);
+ if ((client_fd = accept(server_fd, (struct sockaddr *)&address, &addr_len)) < 0) {
+ debug_printf("accept failed\n");
+ goto fail;
+ }
+
+ struct client_info *info = malloc(sizeof(struct client_info));
+ info->fd = client_fd;
+ info->id = client_id_counter++;
+ info->addr = address;
+
+ clients[client_fd] = info;
+ debug_printf("new connection accepted client:%d\n", info->id);
+
+ int flags = fcntl(client_fd, F_GETFL, 0);
+ fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
+
+ ev.events = EPOLLIN | EPOLLET;
+ ev.data.fd = client_fd;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) == -1) {
+ debug_printf("epoll_ctl: client_fd\n");
+ goto fail;
+ }
+ } else {
+ int client_fd = events[n].data.fd;
+ // char buffer[BUFFER_SIZE] = {0};
+ struct client_info *info = clients[client_fd];
+ char *buffer = buffers[info->id];
+ memset(buffers[info->id], 0, BUFFER_SIZE);
+ ssize_t read_bytes = read(client_fd, buffer, BUFFER_SIZE);
+
+ if (read_bytes <= 0) {
+ if (read_bytes == 0) {
+ debug_printf("client:%d disconnected\n", info->id);
+ continue;
+ } else {
+ debug_printf("read failed\n");
+ }
+ goto fail;
+ } else {
+ debug_printf("Server received client:%d %s\n", info->id, buffer);
+ write(client_fd, buffer, read_bytes);
+ }
+ }
+ }
+ }
+
+fail:
+ close(server_fd);
+ return -1;
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/echo_test.c b/tools/testing/selftests/xcall_prefetch/echo_test.c
new file mode 100644
index 000000000000..fc7180869f3c
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/echo_test.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "./utils.h"
+
+int main() {
+ pid_t server_pid, client_pid;
+ int server_status, client_status;
+
+ server_pid = fork();
+ if (server_pid == 0) {
+ execl("./echo_server", "./echo_server", NULL);
+ printf("execl echo_server failed\n");
+ exit(EXIT_FAILURE);
+ } else if (server_pid < 0) {
+ printf("fork echo_server failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ sleep(1);
+
+ client_pid = fork();
+ if (client_pid == 0) {
+ execl("./echo_client", "./echo_client", NULL);
+ printf("execl client failed\n");
+ exit(EXIT_FAILURE);
+ } else if (client_pid < 0) {
+ printf("fork echo_client failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ waitpid(client_pid, &client_status, 0);
+
+ kill(server_pid, SIGTERM);
+ waitpid(server_pid, &server_status, 0);
+
+ if (WIFEXITED(client_status)) {
+ int exit_code = WEXITSTATUS(client_status);
+ if (exit_code == 0) {
+ printf("echo test %sok%s.\n", ANSI_COLOR_GREEN, ANSI_COLOR_RESET);
+ exit(EXIT_SUCCESS);
+ } else {
+ printf("echo test %sfailed%s.\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ printf("echo test %sfailed%s.\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
+ exit(EXIT_FAILURE);
+ }
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/mulit_close_test.c b/tools/testing/selftests/xcall_prefetch/mulit_close_test.c
new file mode 100644
index 000000000000..fae0407e8473
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/mulit_close_test.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+
+#include "./utils.h"
+
+int main() {
+ int epoll_fd;
+ struct epoll_event ev, events[1];
+ int ret = -1;
+
+ int fds[2];
+ socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+
+ epoll_fd = epoll_create1(0);
+ ev.events = EPOLLIN | EPOLLHUP;
+ ev.data.fd = fds[0];
+ epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fds[0], &ev);
+
+ if (fork() == 0) {
+ close(fds[1]);
+ exit(0);
+ }
+
+ close(fds[1]);
+
+ int nfds = epoll_wait(epoll_fd, events, 1, 1000);
+
+ if (nfds == 1) {
+ if (events[0].events & EPOLLHUP) {
+ ret = 0;
+ }
+ }
+
+ close(fds[0]);
+ close(epoll_fd);
+ if (!ret) {
+ debug_printf("epoll_wait detected EPOLLHUP!\n");
+ printf("multi close test %sok%s.\n", ANSI_COLOR_GREEN, ANSI_COLOR_RESET);
+ return 0;
+ }
+
+ printf("multi close test %sfailed%s.\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
+
+ return -1;
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/multi_write_test.c b/tools/testing/selftests/xcall_prefetch/multi_write_test.c
new file mode 100644
index 000000000000..0af07b742e73
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/multi_write_test.c
@@ -0,0 +1,77 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/wait.h>
+
+#include "./utils.h"
+
+#define NUM_CHILDREN 3
+
+int main() {
+ int epoll_fd, pipe_fd[2];
+ struct epoll_event ev, events[MAX_EVENTS];
+ char buffer[BUFFER_SIZE];
+
+ if (pipe(pipe_fd) == -1) {
+ handle_error("pipe failed\n");
+ }
+
+ epoll_fd = epoll_create1(0);
+ if (epoll_fd == -1) {
+ handle_error("epoll_create1 failed\n");
+ }
+
+ ev.events = EPOLLIN;
+ ev.data.fd = pipe_fd[0];
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipe_fd[0], &ev) == -1) {
+ handle_error("epoll_ctl failed\n");
+ }
+
+ for (int i = 0; i < NUM_CHILDREN; i++) {
+ sleep(1);
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(pipe_fd[0]);
+ char msg[10];
+ sprintf(msg, "%d %d\n", 2*i, 2*i+1);
+ write(pipe_fd[1], msg, strlen(msg));
+ exit(0);
+ } else if (pid < 0) {
+ handle_error("fork failed\n");
+ }
+ }
+
+ sleep(1);
+
+ int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
+ if (nfds == -1) {
+ handle_error("epoll_wait failed\n");
+ }
+
+ debug_printf("epoll_wait returned %d events\n", nfds);
+
+ for (int i = 0; i < nfds; i++) {
+ if (events[i].data.fd == pipe_fd[0]) {
+ ssize_t n = read(pipe_fd[0], buffer, BUFFER_SIZE);
+ if (n > 0) {
+ debug_printf("Received: %.*s", (int)n, buffer);
+ }
+ char *expected_msg = "0 1\n2 3\n4 5\n";
+ if (strncmp(expected_msg, buffer, strlen(expected_msg)) != 0) {
+ printf("multi write test %sfailed%s.\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
+ handle_error("expected msg: %s, buffer: %s", expected_msg, buffer);
+ }
+ }
+ }
+
+ for (int i = 0; i < NUM_CHILDREN; i++) {
+ wait(NULL);
+ }
+
+ close(pipe_fd[0]);
+ close(pipe_fd[1]);
+ close(epoll_fd);
+ printf("multi write test %sok%s.\n", ANSI_COLOR_GREEN, ANSI_COLOR_RESET);
+ return 0;
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/signal_recovery_test.c b/tools/testing/selftests/xcall_prefetch/signal_recovery_test.c
new file mode 100644
index 000000000000..9645f8ffec1b
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/signal_recovery_test.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "./utils.h"
+
+
+int epoll_fd;
+int server_fd;
+volatile sig_atomic_t signal_received = 0;
+
+void signal_handler() {
+ signal_received = 1;
+}
+
+void* worker_thread() {
+ struct epoll_event events[MAX_EVENTS];
+
+ while (!signal_received) {
+ int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
+ if (nfds == -1) {
+ if (errno == EINTR) {
+ debug_printf("epoll_wait interrupted by signal! re-waiting...\n");
+ continue;
+ } else {
+ handle_error("epoll_wait failed\n");
+ }
+ }
+
+ for (int i = 0; i < nfds; i++) {
+ if (events[i].data.fd == server_fd) {
+ struct sockaddr_in address;
+ socklen_t addr_len = sizeof(address);
+ int client_fd = accept(server_fd, (struct sockaddr*)&address, &addr_len);
+ if (client_fd < 0) {
+ handle_error("accept failed\n");
+ }
+
+ struct epoll_event ev;
+ ev.events = EPOLLIN | EPOLLET;
+ ev.data.fd = client_fd;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) < 0) {
+ close(client_fd);
+ handle_error("epoll_ctl failed\n");
+ }
+ } else {
+ char buffer[1024];
+ ssize_t n = read(events[i].data.fd, buffer, sizeof(buffer));
+ if (n <= 0) {
+ if (n < 0) {
+ handle_error("read failed\n");
+ }
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
+ close(events[i].data.fd);
+ debug_printf("closing fd %d\n", events[i].data.fd);
+ } else {
+ buffer[n] = '\0';
+ debug_printf("receive: %s\n", buffer);
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void* client_thread() {
+ sleep(1);
+
+ int client_fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in server_addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(8082),
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+
+ if (connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
+ debug_printf("connect failed\n");
+ goto fail;
+ }
+
+ const char *msg = "hello world.";
+ write(client_fd, msg, strlen(msg));
+ close(client_fd);
+
+ sleep(1);
+
+ debug_printf("Sending SIGUSR1 to interrupt epoll_wait...\n");
+ kill(getpid(), SIGUSR1);
+
+ sleep(1);
+ debug_printf("Testing if epoll_wait can still work after signal...\n");
+
+ client_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
+ debug_printf("connect failed\n");
+ goto fail;
+ }
+
+ const char *msg2 = "hello world again.";
+ if (write(client_fd, msg2, strlen(msg2)) < 0) {
+ goto fail;
+ }
+
+ close(client_fd);
+ return NULL;
+
+fail:
+ close(client_fd);
+ pthread_exit((void *)42);
+}
+
+int main() {
+ pthread_t worker, client;
+
+ signal(SIGUSR1, signal_handler);
+
+ server_fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(8082),
+ .sin_addr.s_addr = INADDR_ANY,
+ };
+ bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
+ listen(server_fd, SOMAXCONN);
+
+ epoll_fd = epoll_create1(0);
+ if (epoll_fd < 0) {
+ handle_error("epoll_create1 failed\n");
+ }
+
+ struct epoll_event ev;
+ ev.events = EPOLLIN | EPOLLET;
+ ev.data.fd = server_fd;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) < 0) {
+ handle_error("epoll_ctl failed\n");
+ }
+
+ pthread_create(&worker, NULL, worker_thread, NULL);
+ pthread_create(&client, NULL, client_thread, NULL);
+
+ void *retval;
+ pthread_join(worker, NULL);
+ pthread_join(client, &retval);
+
+ close(server_fd);
+ close(epoll_fd);
+
+ if (retval != NULL) {
+ printf("signal recovery test %sfailed%s.\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
+ return -1;
+ }
+
+ printf("signal recovery test %sok%s.\n", ANSI_COLOR_GREEN, ANSI_COLOR_RESET);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/thundering_herd_test b/tools/testing/selftests/xcall_prefetch/thundering_herd_test
new file mode 100755
index 0000000000000000000000000000000000000000..8121561474f368c05f8108cddd9d80ac8b05379d
GIT binary patch
literal 21000
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
literal 0
HcmV?d00001
diff --git a/tools/testing/selftests/xcall_prefetch/thundering_herd_test.c b/tools/testing/selftests/xcall_prefetch/thundering_herd_test.c
new file mode 100644
index 000000000000..bf98fcf94b6a
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/thundering_herd_test.c
@@ -0,0 +1,119 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "./utils.h"
+
+#define TIMEOUT_MS 1000
+
+int create_client_connection() {
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(8081),
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+ return fd;
+}
+
+void set_nonblocking(int fd) {
+ int flags = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+}
+
+int main() {
+ int listen_fd, epoll_fd;
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(8081),
+ .sin_addr.s_addr = INADDR_ANY,
+ };
+
+ int shm_id = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | 0666);
+ int tmp_cnt;
+ int *accept_count = (int *)shmat(shm_id, NULL, 0);
+ *accept_count = 0;
+
+ listen_fd = socket(AF_INET, SOCK_STREAM, 0);
+ set_nonblocking(listen_fd);
+ int reuse = 1;
+ setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
+ bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr));
+ listen(listen_fd, SOMAXCONN);
+
+ for (int i = 0; i < NUM_THREADS; i++) {
+ if (fork() == 0) {
+ usleep(1000 * i);
+ epoll_fd = epoll_create1(0);
+ struct epoll_event ev = {
+ .events = EPOLLIN | EPOLLET,
+ .data.fd = listen_fd,
+ };
+ epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev);
+
+ debug_printf("Worker %d waiting...\n", getpid());
+
+ struct epoll_event events[MAX_EVENTS];
+ int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, TIMEOUT_MS);
+
+ if (nfds == -1) {
+ handle_error("epoll_wait failed\n");
+ }
+
+ debug_printf("Worker %d: epoll_wait returned %d events\n", getpid(), nfds);
+
+ int client_fd = accept(listen_fd, NULL, NULL);
+ if (client_fd >= 0) {
+ debug_printf("Worker %d accepted a connection!\n", getpid());
+ __sync_fetch_and_add(accept_count, 1);
+ close(client_fd);
+ } else if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ debug_printf("accept failed\n");
+ }
+
+ close(epoll_fd);
+ exit(0);
+ }
+ }
+
+ sleep(1);
+
+ debug_printf("Parent creating client connection...\n");
+ int client_fd = create_client_connection();
+ sleep(1);
+ close(client_fd);
+
+ int waited = 0;
+ for (int i = 0; i < NUM_THREADS; i++) {
+ if (wait(NULL) < 0 && errno == ECHILD) {
+ break;
+ }
+ waited++;
+ }
+
+ tmp_cnt = *accept_count;
+ debug_printf("Total successful accepts: %d\n", tmp_cnt);
+
+ shmdt(accept_count);
+ shmctl(shm_id, IPC_RMID, NULL);
+ close(listen_fd);
+
+ if (tmp_cnt == 1) {
+ printf("thundering herd test %sok%s.\n", ANSI_COLOR_GREEN, ANSI_COLOR_RESET);
+ return 0;
+ } else {
+ printf("thundering herd test %sfailed%s.\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
+ return -1;
+ }
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/utils.h b/tools/testing/selftests/xcall_prefetch/utils.h
new file mode 100644
index 000000000000..58959c564119
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/utils.h
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#define ANSI_COLOR_GREEN "\x1b[32m"
+#define ANSI_COLOR_RED "\x1b[31m"
+#define ANSI_COLOR_RESET "\x1b[0m"
+
+#define PORT 8080
+#define BUFFER_SIZE 4097
+#define MAX_EVENTS 100
+#define MAX_MSGS 4
+#define NUM_THREADS 4
+
+#ifdef DEBUG
+#define debug_printf(fmt, ...) (printf(fmt, ##__VA_ARGS__))
+#else
+#define debug_printf(fmt, ...) (void)fmt
+#endif
+
+struct client_info {
+ int fd;
+ int id;
+ struct sockaddr_in addr;
+};
+
+void handle_error(const char *format, ...)
+{
+ printf("%s", format);
+ exit(EXIT_FAILURE);
+}
+
+char* generate_number_string(int length)
+{
+ if (length <= 0) {
+ printf("\nnumber string length invalid\n");
+ return NULL;
+ }
+
+ char* result = (char*)malloc((length + 1) * sizeof(char));
+ if (result == NULL) {
+ printf("\nnumber string malloc failed\n");
+ return NULL;
+ }
+
+ for (int i = 0; i < length; i++) {
+ result[i] = '0' + (i % 10);
+ }
+
+ result[length] = '\0';
+
+ return result;
+}
\ No newline at end of file
diff --git a/tools/testing/selftests/xcall_prefetch/xcall_prefetch_test.sh b/tools/testing/selftests/xcall_prefetch/xcall_prefetch_test.sh
new file mode 100755
index 000000000000..e9a990dac69a
--- /dev/null
+++ b/tools/testing/selftests/xcall_prefetch/xcall_prefetch_test.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+RED="\033[31m"
+GREEN="\033[32m"
+RESET="\033[0m"
+
+make clean
+make
+
+if [ -e "/proc/$$/xcall" ]; then
+ echo 22 > "/proc/$$/xcall"
+ echo @22 > "/proc/$$/xcall"
+ echo "enable xcall epoll prefetch, pid is $$."
+else
+ echo "Warnning: not enable xcall."
+fi
+
+./echo_test
+sleep 1
+
+./multi_write_test
+sleep 1
+
+./thundering_herd_test
+sleep 1
+
+./mulit_close_test
+sleep 1
+
+./signal_recovery_test
+sleep 1
+
+if [ $? -eq 0 ]; then
+ echo -e "${GREEN}tests passed successfully.${RESET}"
+ exit 0
+else
+ echo -e "${RED}tests failed.${RESET}"
+ exit 1
+fi
\ No newline at end of file
--
2.34.1
2
1