- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我的目标是将静态结构加载到 L1D 缓存中。之后使用这些结构成员执行一些操作,并在完成操作后运行 invd
丢弃所有修改过的缓存行。所以基本上我想在缓存内创建一个安全的环境,这样在缓存内执行操作时,数据就不会泄漏到 RAM 中。
为此,我有一个内核模块。我在结构的成员上放置了一些固定值。然后我禁用抢占,禁用所有其他 CPU(当前 CPU 除外)的缓存,禁用中断,然后使用 __builtin_prefetch()
将我的静态结构加载到缓存中。之后,我用新值覆盖之前放置的固定值。之后,我执行 invd
(清除修改后的缓存行)然后启用缓存到所有其他 CPU,启用中断和启用抢占。我的理由是,当我在原子模式下这样做时,INVD
将删除所有更改。从原子模式回来后,我应该看到我之前放置的原始固定值。然而,这并没有发生。退出原子模式后,我可以看到用于覆盖先前放置的固定值的值。这是我的模块代码,
奇怪的是,在重新启动 PC 后,我的输出发生了变化,我只是不明白为什么。现在,我根本没有看到任何变化。我正在发布完整的代码,包括@Peter Cordes 建议的一些修复,
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Author");
MODULE_DESCRIPTION("test INVD");
static struct CACHE_ENV{
unsigned char in[128];
unsigned char out[128];
}cacheEnv __attribute__((aligned(64)));
#define cacheEnvSize (sizeof(cacheEnv)/64)
//#define change "Hello"
unsigned char change[]="hello";
void disCache(void *p){
__asm__ __volatile__ (
"wbinvd\n"
"mov %%cr0, %%rax\n\t"
"or $(1<<30), %%eax\n\t"
"mov %%rax, %%cr0\n\t"
"wbinvd\n"
::
:"%rax"
);
printk(KERN_INFO "cpuid %d --> cache disable\n", smp_processor_id());
}
void enaCache(void *p){
__asm__ __volatile__ (
"mov %%cr0, %%rax\n\t"
"and $~(1<<30), %%eax\n\t"
"mov %%rax, %%cr0\n\t"
::
:"%rax"
);
printk(KERN_INFO "cpuid %d --> cache enable\n", smp_processor_id());
}
int changeFixedValue (struct CACHE_ENV *env){
int ret=1;
//memcpy(env->in, change, sizeof (change));
//memcpy(env->out, change,sizeof (change));
strcpy(env->in,change);
strcpy(env->out,change);
return ret;
}
void fillCache(unsigned char *p, int num){
int i;
//unsigned char *buf = p;
volatile unsigned char *buf=p;
for(i=0;i<num;++i){
/*
asm volatile(
"movq $0,(%0)\n"
:
:"r"(buf)
:
);
*/
//__builtin_prefetch(buf,1,1);
//__builtin_prefetch(buf,0,3);
*buf += 0;
buf += 64;
}
printk(KERN_INFO "Inside fillCache, num is %d\n", num);
}
static int __init device_init(void){
unsigned long flags;
int result;
struct CACHE_ENV env;
//setup Fixed values
char word[] ="0xabcd";
memcpy(env.in, word, sizeof(word) );
memcpy(env.out, word, sizeof (word));
printk(KERN_INFO "env.in fixed is %s\n", env.in);
printk(KERN_INFO "env.out fixed is %s\n", env.out);
printk(KERN_INFO "Current CPU %s\n", smp_processor_id());
// start atomic
preempt_disable();
smp_call_function(disCache,NULL,1);
local_irq_save(flags);
asm("lfence; mfence" ::: "memory");
fillCache(&env, cacheEnvSize);
result=changeFixedValue(&env);
//asm volatile("invd\n":::);
asm volatile("invd\n":::"memory");
// exit atomic
smp_call_function(enaCache,NULL,1);
local_irq_restore(flags);
preempt_enable();
printk(KERN_INFO "After: env.in is %s\n", env.in);
printk(KERN_INFO "After: env.out is %s\n", env.out);
return 0;
}
static void __exit device_cleanup(void){
printk(KERN_ALERT "Removing invd_driver.\n");
}
module_init(device_init);
module_exit(device_cleanup);
我得到以下输出:
[ 3306.345292] env.in fixed is 0xabcd
[ 3306.345321] env.out fixed is 0xabcd
[ 3306.345322] Current CPU (null)
[ 3306.346390] cpuid 1 --> cache disable
[ 3306.346611] cpuid 3 --> cache disable
[ 3306.346844] cpuid 2 --> cache disable
[ 3306.347065] cpuid 0 --> cache disable
[ 3306.347313] cpuid 4 --> cache disable
[ 3306.347522] cpuid 5 --> cache disable
[ 3306.347755] cpuid 6 --> cache disable
[ 3306.351235] Inside fillCache, num is 4
[ 3306.352250] cpuid 3 --> cache enable
[ 3306.352997] cpuid 5 --> cache enable
[ 3306.353197] cpuid 4 --> cache enable
[ 3306.353220] cpuid 6 --> cache enable
[ 3306.353221] cpuid 2 --> cache enable
[ 3306.353221] cpuid 1 --> cache enable
[ 3306.353541] cpuid 0 --> cache enable
[ 3306.353608] After: env.in is hello
[ 3306.353609] After: env.out is hello
我的
Makefile
是
obj-m += invdMod.o
CFLAGS_invdMod.o := -o0
invdMod-objs := disable_cache.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
rm -f *.o
有没有想过我做错了什么? 正如我之前所说,我希望我的输出保持不变。
__builtin_prefetch()
没有将结构放入缓存中。另一种将内容放入缓存的方法是设置
write-back
区域在
MTRR
的帮助下&
PAT
.但是,我对如何实现这一目标一无所知。我找到了
12.6. Creating MTRRs from a C programme using ioctl()’s显示如何创建
MTRR
区域,但我不知道如何将结构的地址与该区域绑定(bind)。
Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
内核版本:
Linux xxx 4.4.0-200-generic #232-Ubuntu SMP Wed Jan 13 10:18:39 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
GCC 版本:
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
我已经用
-O0
编译了这个模块范围
echo off > /sys/devices/system/cpu/smt/control
关闭了超线程.之后,运行我的模块似乎是,
changeFixedValue()
&
fillCache()
没有被调用。
[ 3971.480133] env.in fixed is 0xabcd
[ 3971.480134] env.out fixed is 0xabcd
[ 3971.480135] Current CPU 3
[ 3971.480739] cpuid 2 --> cache disable
[ 3971.480956] cpuid 1 --> cache disable
[ 3971.481175] cpuid 0 --> cache disable
[ 3971.482771] cpuid 2 --> cache enable
[ 3971.482774] cpuid 0 --> cache enable
[ 3971.483043] cpuid 1 --> cache enable
[ 3971.483065] After: env.in is 0xabcd
[ 3971.483066] After: env.out is 0xabcd
最佳答案
打电话printk
看起来很不安全在fillCache 的底部。您将要经营更多的商店,然后是 invd
, 所以任何修改 printk
对内核数据结构(如日志缓冲区)的修改可能会被写回 DRAM,或者如果它们在缓存中仍然脏,则可能会失效。如果某些但不是所有存储都进入 DRAM(因为缓存容量有限),您可能会使内核数据结构处于不一致状态。
我猜您当前禁用 HT 的测试表明一切都比您希望的要好,包括丢弃由 printk
完成的商店 ,以及丢弃由 changeFixedValue
完成的商店.这可以解释为什么代码完成后没有留给用户空间读取的日志消息。
要对此进行测试,您最好是 clflush
printk 所做的一切,但没有简单的方法可以做到这一点。也许 wbinvd
然后 changeFixedValue
然后 invd
. (你没有在这个核心上进入无填充模式,所以 fillCache
不是你的商店/invd 想法工作所必需的,见下文。)
启用超线程:
CR0.CD 是每个物理核心,所以让你的 HT 兄弟核心禁用缓存也意味着隔离核心的 CD=1。 因此,启用 HT 后,即使在隔离核心上,您也处于无填充模式。
HT 关闭后,隔离内核仍然正常。
编译时和运行时重新排序asm volatile("invd\n":::);
没有 "memory"
clobber 告诉编译器它可以重新排序。内存操作。显然,这不是您的问题,但这是您应该修复的错误。
放 asm("mfence; lfence" ::: "memory");
可能也是个好主意就在之前 fillCache
, 以确保任何缓存未命中的加载和存储都不会仍在运行中,并且可能会在您的代码运行时分配新的缓存行。或者甚至可能是像 asm("xor %eax,%eax; cpuid" ::: "eax", "ebx", "ecx", "edx", "memory");
这样的完全序列化指令,但我不知道 CPUID 阻止了哪个 mfence;围栏不会。
标题问题:触摸内存将其带入缓存
PREFETCHT0(进入 L1d 缓存)是 __builtin_prefetch(p,0,3);
. This answer显示 args 如何映射到指令;您正在使用 prefetchw
(写意向)或者我认为 prefetcht1
(L2 缓存)取决于编译器选项。
但实际上,由于您需要这样做以确保正确性,因此您不应该使用硬件在繁忙时可以丢弃的可选提示。 mfence; lfence
会让硬件不太可能真的很忙,但仍然不是一个坏主意。
使用 volatile
阅读喜欢 READ_ONCE
让 GCC 发出加载指令。或使用 volatile char *buf
与 *buf |= 0;
或者真正的 RMW 而不是预取,以确保该行是独家拥有的,而不必让 GCC 发出 prefetchw
.
也许值得运行 fillCache 几次,只是为了更确保每一行都处于您想要的状态。但是由于您的 env 小于 4k,因此每一行都将位于 L1d 缓存中的不同集合中,因此在分配另一行时不会有一行被丢弃的风险(除非 L3 缓存的哈希函数中有别名?但即便如此,伪 LRU 驱逐应该可靠地保持最近的行。)
将您的数据按 128 对齐,这是一对对齐的缓存行static struct CACHE_ENV { ... } cacheEnv;
不能保证与缓存行大小对齐;你错过了 C11 _Alignas(64)
或 GNU C __attribute__((aligned(64)))
.所以它可能跨越sizeof(T)/64
线。或者,为了更好的衡量,将 L2 相邻行预取器对齐 128。 (在这里你可以而且应该简单地对齐你的缓冲区,但 The right way to use function _mm_clflush to flush a large struct 展示了如何循环遍历任意大小的可能未对齐结构的每个缓存行。)
这并不能解释您的问题,因为唯一可能被遗漏的部分是 env.out
的最后最多 48 个字节。 . (我认为默认 ABI 规则下全局结构将按 16 对齐。)而且您只打印每个数组的前几个字节。
更简单的方法:memset(0) 避免将数据泄漏回 DRAM
顺便说一句,用 0
覆盖你的缓冲区完成后通过 memset 还应该防止您的数据像 INVD 一样可靠地写回 DRAM,但速度更快。 (也许是通过 asm 的手册 rep stosb
以确保它不能像死商店一样优化)。
无填充模式在这里也可能有用,以阻止缓存未命中驱逐现有行。 AFAIK,这基本上锁定了缓存,因此不会发生新的分配,因此不会被驱逐。 (但您可能无法读取或写入其他正常内存,尽管您可以将结果留在寄存器中。)
无填充模式(对于当前核心)将使在重新启用分配之前使用 memset 清除缓冲区绝对安全;在导致驱逐期间没有缓存未命中的风险。尽管如果您的 fillCache 实际上工作正常并且在您开始工作之前让您的所有行都进入 MESI Modified 状态,您的负载和存储将在 L1d 缓存中命中,而不会存在驱逐任何缓冲行的风险。
如果您担心 DRAM 内容(而不是总线信号),那么 在memset之后的每一行clflushopt都会减少漏洞的窗口 . (或者如果 0
对您不起作用,则来自原始副本的 memcpy,但希望您可以只在私有(private)副本中工作并且保持原稿不变。使用当前方法始终可以进行杂散回写,因此我不想依靠它来确定总是保持一个大缓冲区未修改。)
不要将 NT 存储用于手动 memset 或 memcpy:这可能会在 NT 存储之前刷新“ secret ”脏数据。一种选择是使用普通商店或 rep stosb
memset(0) ,然后用 NT 存储再次循环。或者也许每行做 8 次 movq 普通存储,然后是 8 次 movnti,所以你在继续之前对同一行背靠背做这两件事。
为什么要fillCache?
如果您不使用无填充模式,那么在写入行之前是否缓存这些行都无关紧要。当 invd
时,您只需要您的写入在缓存中变脏运行,即使它们是从缓存中丢失的商店中获得的,这也应该是正确的。
您在 fillCache
之间已经没有像 mfence 这样的任何障碍和 changeFixedValue
,这很好,但意味着当您弄脏缓存时,任何因启动缓存而导致的缓存未命中仍在进行中。
INVD 本身 is serializing ,所以在丢弃缓存内容之前,它应该等待存储离开存储缓冲区。 (所以将 mfence;lfence
放在你的工作之后,在 INVD 之前,应该没有任何区别。)换句话说,INVD 应该丢弃仍在存储缓冲区中的可缓存存储,以及脏缓存行,除非提交其中的一些商店碰巧驱逐任何东西。
关于c - 如何将结构显式加载到 L1d 缓存中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66772632/
我目前正在尝试基于哈希表构建字典。逻辑是:有一个名为 HashTable 的结构,其中包含以下内容: HashFunc HashFunc; PrintFunc PrintEntry; CompareF
如果我有一个指向结构/对象的指针,并且该结构/对象包含另外两个指向其他对象的指针,并且我想删除“包含这两个指针的对象而不破坏它所持有的指针”——我该怎么做这样做吗? 指向对象 A 的指针(包含指向对象
像这样的代码 package main import "fmt" type Hello struct { ID int Raw string } type World []*Hell
我有一个采用以下格式的 CSV: Module, Topic, Sub-topic 它需要能够导入到具有以下格式的 MySQL 数据库中: CREATE TABLE `modules` ( `id
通常我使用类似的东西 copy((uint8_t*)&POD, (uint8_t*)(&POD + 1 ), back_inserter(rawData)); copy((uint8_t*)&PODV
错误 : 联合只能在具有兼容列类型的表上执行。 结构(层:字符串,skyward_number:字符串,skyward_points:字符串)<> 结构(skyward_number:字符串,层:字符
我有一个指向结构的指针数组,我正在尝试使用它们进行 while 循环。我对如何准确初始化它并不完全有信心,但我一直这样做: Entry *newEntry = malloc(sizeof(Entry)
我正在学习 C,我的问题可能很愚蠢,但我很困惑。在这样的函数中: int afunction(somevariables) { if (someconditions)
我现在正在做一项编程作业,我并没有真正完全掌握链接,因为我们还没有涉及它。但是我觉得我需要它来做我想做的事情,因为数组还不够 我创建了一个结构,如下 struct node { float coef;
给定以下代码片段: #include #include #define MAX_SIZE 15 typedef struct{ int touchdowns; int intercepti
struct contact list[3]; int checknullarray() { for(int x=0;x<10;x++) { if(strlen(con
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: Empty “for” loop in Facebook ajax what does AJAX call
我刚刚在反射器中浏览了一个文件,并在结构构造函数中看到了这个: this = new Binder.SyntaxNodeOrToken(); 我以前从未见过该术语。有人能解释一下这个赋值在 C# 中的
我经常使用字符串常量,例如: DICT_KEY1 = 'DICT_KEY1' DICT_KEY2 = 'DICT_KEY2' ... 很多时候我不介意实际的文字是什么,只要它们是独一无二的并且对人类读
我是 C 的新手,我不明白为什么下面的代码不起作用: typedef struct{ uint8_t a; uint8_t* b; } test_struct; test_struct
您能否制作一个行为类似于内置类之一的结构,您可以在其中直接分配值而无需调用属性? 前任: RoundedDouble count; count = 5; 而不是使用 RoundedDouble cou
这是我的代码: #include typedef struct { const char *description; float value; int age; } swag
在创建嵌套列表时,我认为 R 具有对列表元素有用的命名结构。我有一个列表列表,并希望应用包含在任何列表中的每个向量的函数。 lapply这样做但随后剥离了列表的命名结构。我该怎么办 lapply嵌套列
我正在做一个用于学习目的的个人组织者,我从来没有使用过 XML,所以我不确定我的解决方案是否是最好的。这是我附带的 XML 文件的基本结构:
我是新来的 nosql概念,所以当我开始学习时 PouchDB ,我找到了这个转换表。我的困惑是,如何PouchDB如果可以说我有多个表,是否意味着我需要创建多个数据库?因为根据我在 pouchdb
我是一名优秀的程序员,十分优秀!