Как просмотреть и очистить содержимое физической страницы ядра Linux?
У меня следующая проблема с модулем ядра Linux (упрощенный пример без проверки ошибок):
addr = (uint32_t*) mmap(NULL, 4096, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0);
for (uint32_t i = 0; i < 1024; i++)
addr[i] = 0xCCCCDDDDD;
munmap(addr, 4096);
addr = (uint32_t*) mmap(NULL, 4096, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0);
for (uint32_t i = 0; i < 1024; i++)
assert(addr[i] == 0xCCCCDDDDD)
munmap(addr, 4096);
На некоторых архитектурах (Intel x86) это удается. На других архитектурах (ARM) код заканчивает тем, что поражает утверждение (хотя конкретное место в массиве, где это получает попадания, изменяется).
У меня есть доступ к физическим страницам (struct page
) этого буфера. Я пробовал звонить flush_dcache_page
во время близкого обратного вызова vm_operations_struct
Однако это не мешает приведенному выше коду попасть в утверждение. Это странно, так как я также должен установить vm_page_prot
в pgprot_noncached
или же pgprot_writecombine
при создании отображения.
Три вопроса:
1) Что может быть причиной поведения, которое я описал?
2) Можно ли как-то просмотреть реальные данные в памяти в struct page
? я знаю kmap
создаст отображение ядра, но оно также не может быть записано на физическую страницу и застряло в каком-то кеше. kmap
При закрытии область виртуальной памяти указывает, что некоторые части памяти заполнены нулями. Я пытался написать свой магический номер с kmap
/kunmap
:
v = kmap(pages[i]);
for (j = 0; j < (PAGE_SIZE / sizeof(uint32_t)); ++j) {
printk("v[%u] before: 0x%08X ", j, v[j]);
v[j] = 0xCCCCDDDD;
printk("v[%u] after: 0x%08X\n", j, v[j]);
}
kunmap(pages[i]);
flush_dcache_page(pages[i]);
Это после того, как пользовательское пространство пишет. До этого операторы printk указывали, что некоторые части буфера заполняются моим магическим числом, а после него всегда, т.е.
v[3] before: 0xCCCCDDDD v[3] after: 0xCCCCDDDD
v[4] before: 0xCCCCDDDD v[4] after: 0xCCCCDDDD
v[5] before: 0xCCCCDDDD v[5] after: 0xCCCCDDDD
v[6] before: 0xCCCCDDDD v[6] after: 0xCCCCDDDD
v[7] before: 0xCCCCDDDD v[7] after: 0xCCCCDDDD
v[8] before: 0xCCCCDDDD v[8] after: 0xCCCCDDDD
v[9] before: 0xCCCCDDDD v[9] after: 0xCCCCDDDD
v[10] before: 0xCCCCDDDD v[10] after: 0xCCCCDDDD
v[11] before: 0xCCCCDDDD v[11] after: 0xCCCCDDDD
v[12] before: 0xCCCCDDDD v[12] after: 0xCCCCDDDD
v[13] before: 0xCCCCDDDD v[13] after: 0xCCCCDDDD
v[14] before: 0xCCCCDDDD v[14] after: 0xCCCCDDDD
v[15] before: 0xCCCCDDDD v[15] after: 0xCCCCDDDD
v[16] before: 0 v[16] after: 0xCCCCDDDD
v[17] before: 0 v[17] after: 0xCCCCDDDD
v[18] before: 0 v[18] after: 0xCCCCDDDD
v[19] before: 0 v[19] after: 0xCCCCDDDD
v[20] before: 0 v[20] after: 0xCCCCDDDD
v[21] before: 0 v[21] after: 0xCCCCDDDD
v[22] before: 0 v[22] after: 0xCCCCDDDD
v[23] before: 0 v[23] after: 0xCCCCDDDD
v[24] before: 0 v[24] after: 0xCCCCDDDD
v[25] before: 0 v[25] after: 0xCCCCDDDD
v[26] before: 0 v[26] after: 0xCCCCDDDD
v[27] before: 0 v[27] after: 0xCCCCDDDD
v[28] before: 0 v[28] after: 0xCCCCDDDD
v[29] before: 0 v[29] after: 0xCCCCDDDD
v[30] before: 0 v[30] after: 0xCCCCDDDD
v[31] before: 0 v[31] after: 0xCCCCDDDD
v[32] before: 0 v[32] after: 0xCCCCDDDD
v[33] before: 0 v[33] after: 0xCCCCDDDD
v[34] before: 0 v[34] after: 0xCCCCDDDD
v[35] before: 0 v[35] after: 0xCCCCDDDD
v[36] before: 0 v[36] after: 0xCCCCDDDD
v[37] before: 0 v[37] after: 0xCCCCDDDD
Тем не менее, даже после этих дополнительных записей, пользовательское пространство все еще попадает в утверждения, указывающие, что записи не были сброшены в физическую память. Вот почему я хотел бы изучить физическую память напрямую.
3) Есть ли надежный способ очистить или аннулировать все кэши в ядре Linux, если struct page
?
2 ответа
Как и предполагалось, происходили некоторые проблемы с кэшированием. Оказывается, содержимое страницы все еще находилось в кеше от предыдущего использования. Произошла гонка между ожидающими записями и записями в пользовательском пространстве. Решением было очистить кеш, когда модуль ядра извлекал страницы.
В моем конкретном случае, похоже, что flush_dcache_page не работает. В ARM помогло использование DMA API - то есть dma_sync_sg_for_device.
Это выглядит как неопределенное поведение: вы отображаете 1024 байта
addr = (uint32_t*) mmap(NULL, 1024, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0);
затем 4096 байтов в цикле.
for (uint32_t i = 0; i < 1024; i++)
assert(addr[i] == 0xCCCCDDDDD)
Так что длина в вашем случае неверна, от mmap man:
Содержимое сопоставления файлов (в отличие от анонимного сопоставления; см. MAP_ANONYMOUS ниже) инициализируется с использованием байтов длины, начинающихся со смещения смещения в файле (или другом объекте), на который ссылается дескриптор файла fd.