amarao (amarao_san) wrote,
amarao
amarao_san

The Kernel (part6)

Чтение memblock.c навело на неожиданную мысль. Я делал суммирование всех reserved областей по выводу memblock при включенном memblock=debug в параметрах ядра.

А когда именно это происходит? И достаточно ли этого?

Вкодим в наш несчастный код в nobootmem.c ещё один кусок отладки - в функцию free_low_memory_core_early (где, как мы выяснили, возвращаемое значение count являетс неверно посчитанным и скоро превратится в totalrampages)
   printk(KERN_ERR "free_low_memory_core_early, count=%lu\n", (unsigned long)count);
    printk(KERN_ERR "repeat memblock dump\n");
    __memblock_dump_all();
    return count;


Грузимся с этим ядром, получаем:
[    0.000000] repeat memblock dump
[    0.000000] MEMBLOCK configuration:
[    0.000000]  memory size = 0xff90000 reserved size = 0x254b5e2
[    0.000000]  memory.cnt  = 0x2
[    0.000000]  memory[0x0]	[0x00000000010000-0x0000000009ffff], 0x90000 bytes on node 0
[    0.000000]  memory[0x1]	[0x00000000100000-0x0000000fffffff], 0xff00000 bytes on node 0
[    0.000000]  reserved.cnt  = 0x17
[    0.000000]  reserved[0x0]	[0x0000000009a000-0x0000000009ffff], 0x6000 bytes
[    0.000000]  reserved[0x1]	[0x00000001000000-0x00000001a5cfff], 0xa5d000 bytes
[    0.000000]  reserved[0x2]	[0x00000001deb000-0x00000001e4efff], 0x64000 bytes
[    0.000000]  reserved[0x3]	[0x00000001e6d000-0x0000000339afff], 0x152e000 bytes
[    0.000000]  reserved[0x4]	[0x0000000341b000-0x0000000341dfff], 0x3000 bytes
[    0.000000]  reserved[0x5]	[0x00000003421000-0x0000000343cfff], 0x1c000 bytes
[    0.000000]  reserved[0x6]	[0x00000007ffffc0-0x00000007ffffff], 0x40 bytes
[    0.000000]  reserved[0x7]	[0x0000000f200000-0x0000000f5fffff], 0x400000 bytes
[    0.000000]  reserved[0x8]	[0x0000000f7fb000-0x0000000f7fefff], 0x4000 bytes
[    0.000000]  reserved[0x9]	[0x0000000fc00000-0x0000000fc1bfff], 0x1c000 bytes
[    0.000000]  reserved[0xa]	[0x0000000fee88c0-0x0000000ff48b0f], 0x60250 bytes
[    0.000000]  reserved[0xb]	[0x0000000ff48b40-0x0000000ff48b47], 0x8 bytes0x8
[    0.000000]  reserved[0xc]	[0x0000000ff48b80-0x0000000ff48b83], 0x4 bytes
[    0.000000]  reserved[0xd]	[0x0000000ff48bc0-0x0000000ff48bc7], 0x8 bytes
[    0.000000]  reserved[0xe]	[0x0000000ff48c00-0x0000000ff48c07], 0x8 bytes
[    0.000000]  reserved[0xf]	[0x0000000ff48c40-0x0000000ff4acbe], 0x207f bytes
[    0.000000]  reserved[0x10]	[0x0000000ff4acc0-0x0000000ff4ad3e], 0x7f bytes
[    0.000000]  reserved[0x11]	[0x0000000ff4ad40-0x0000000ff4ad5f], 0x20 bytes
[    0.000000]  reserved[0x12]	[0x0000000ff4ad80-0x0000000ff4ade7], 0x68 bytes
[    0.000000]  reserved[0x13]	[0x0000000ff4ae00-0x0000000ff4ae67], 0x68 bytes
[    0.000000]  reserved[0x14]	[0x0000000ff4ae80-0x0000000ff4aee7], 0x68 bytes
[    0.000000]  reserved[0x15]	[0x0000000ff4af00-0x0000000ff4afdf], 0xe0 bytes
[    0.000000]  reserved[0x16]	[0x0000000ff4b000-0x0000000fffffff], 0xb5000 bytes

Забавно, вывод отличается от отладочного вывода самого memblock, видимо, дело в memblock_get_region_node(rgn) != MAX_NUMNODES, впрочем, нас это пока не волнует.

Суммируем (вывод настолько любезен, что прямо бери и в авк засовывай).
dmesg |fgrep " reserved["|awk '{a=a+$5}END{print a}'

Получаем 72832482, то есть 72Мб. Странно. Как так? Почему у нас reserved оказалось больше, чем сожранная непонятно куда память? Ведь мы:

for_each_free_mem_range(i, MAX_NUMNODES, &start, &end, NULL){
t=__free_memory_core(start, end);
count +=t;
, а потом
__memblock_dump_all();

При этом у нас разница между max_pfn и count (с поправкой на то, что count и max_pfn - в страницах) составляет 38196k. Ну никак не сходится.

Заметим, в отладочном выводе мы не видим ни одного free. Потому что нам сделали free_memory_core для каждого такого блока?

Сравним вывод dump_all до вызова цикла с free_memory_core и без него.

Он совпадает. Однако, возможно, нам надо сделать его (первый дамп) до выполнения reset_node_lowmem_managed_pages.

Кстати, я невнимателен, отладка уже содержит все нужные нам цифры, awk не обязателен:

memory size = 0xff90000 reserved size = 0x254b5e2
Что по-русски звучит так: memory_size=256Мб-458752 байт (448кб), что соответствует 448k absent в выводе Memory:, и объясняется синдромом 640кб.

reserved size у нас тут 39Мб (та самая спёртая память). Стоп... Кажется, мой мегаскрипт с авком принёс больше зла, чем пользы.

Проверяем вручную:
0x6000+0xa5d000+0x64000+0x152e000+0x3000+0x1c000+0x40+0x400000+0x4000+0x1c000+0x60250+0x8+0x4+0x8+0x8+0x207f+0x7f+0x20+0x68+0x68+0x68+0xe0+0xb5000=39106018
Ну точно, авковый скрипт сделал бяку. Вероятнее всего, дважды посчитал reserved.

Ладно, дело не в этом. Итого: у нас reserved с точки зрения dump у memblock это именно та спёртая память, которую мы ищем. Значит, её кто-то внёс в reserved, значит этот кто-то виноват.

Но к этому времени ядро собралось и скопировалось на хост, так что можно сравнить вывод dump до и после reset_node_lowmem_managed_pages.

Вывод не отличается, но это уже никого не волнует.

Итак, наша проблема состоит в том, что кто-то делает memblock_reserve слишком обильно. Я не уверен, что на всех этапах вызова memblock можно делать printk, но попытка не пытка.

Код функции:

int __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size)
{
    struct memblock_type *_rgn = &memblock.reserved;

    memblock_dbg("memblock_reserve: [%#016llx-%#016llx] %pF\n",
             (unsigned long long)base,
             (unsigned long long)base + size,
             (void *)_RET_IP_);

    return memblock_add_region(_rgn, base, size, MAX_NUMNODES);
}

Внезапно, отладку уже добавили без нас. Собственно, это и есть то, что мы видим в dmesg:
напр.:
memblock_reserve: [0x0000000ff488c0-0x0000000ff48940] __alloc_memory_core_early+0x56/0x70

Кстати, конструкцию (void *)_RET_IP_ стоит запомнить...

Итак, они утверждают, что все вызовы memblock_reserve уже залоггированы. JIC:

[    0.000000] memblock_reserve: [0x00000001a50000-0x00000001a5d000] setup_arch+0x5e4/0xb80
[    0.000000] memblock_reserve: [0x0000000009a000-0x000000000a0000] setup_real_mode+0x65/0x186
[    0.000000] memblock_reserve: [0x00000001deb000-0x00000001e4f000] xen_mapping_pagetable_reserve+0x15/0x57
[    0.000000] memblock_reserve: [0x0000000ffff000-0x00000010000000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000fbff000-0x0000000ffff000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x00000007ffffc0-0x00000008000000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000f7ff000-0x0000000fbff000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000f200000-0x0000000f600000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000f7fe000-0x0000000f7ff000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000f7fd000-0x0000000f7fe000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000f7fc000-0x0000000f7fd000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000f7fb000-0x0000000f7fc000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ffe7000-0x0000000ffff000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ffcf000-0x0000000ffe7000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff4f000-0x0000000ffcf000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff4e000-0x0000000ff4f000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff4d000-0x0000000ff4e000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff4c000-0x0000000ff4d000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff4b000-0x0000000ff4c000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff4af00-0x0000000ff4afe0] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff4ae80-0x0000000ff4aee8] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff4ae00-0x0000000ff4ae68] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff4ad80-0x0000000ff4ade8] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff4ad40-0x0000000ff4ad60] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff4acc0-0x0000000ff4ad3f] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff4ac40-0x0000000ff4acbf] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff49c40-0x0000000ff4ac40] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff48c40-0x0000000ff49c40] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000fc00000-0x0000000fe00000] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff48c00-0x0000000ff48c08] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff48bc0-0x0000000ff48bc8] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff48b80-0x0000000ff48b84] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff48b40-0x0000000ff48b48] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff489c0-0x0000000ff48b10] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff48940-0x0000000ff489c0] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff488c0-0x0000000ff48940] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff48c40-0x0000000ff4ac40] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000ff088c0-0x0000000ff488c0] __alloc_memory_core_early+0x56/0x70
[    0.000000] memblock_reserve: [0x0000000fee88c0-0x0000000ff088c0] __alloc_memory_core_early+0x56/0x70

И нам таки надо это культурно посчитать. Наплевав на сприптование, копипастим, конвертим в питоновское вида print 0x00000001a50000-0x00000001a5d000

Суммируем результат... -16336354, то бишь 16Мб. Что за подлость? Перепроверяю руками (мало ли, нет - именно 16М).

Но ведь reserved потом говорит про 38? Подлость зарылась тут?
Вопрос: а почему я решил, что единственный метод резервировать что-либо в memblock - это memblock_reserve?

Поскольку вывод dump согласуется с реальными наблюдениями, посмотрим, как он узнаёт где что и как?

Ответ:
    pr_info(" memory size = %#llx reserved size = %#llx\n",
        (unsigned long long)memblock.memory.total_size,
        (unsigned long long)memblock.reserved.total_size);

Другими словами, memblock.reserved.total_size.

А давайте сдеаем подлую вещь, и включим в дебаг-вывод memblock вывод memblock.reserved.total_size. При этом мы получим инкрементальный счётчик. Если на последней строчке memblock_reserve дебага мы будем иметь счётчик, отличающийся от финальных показателей, значит что-то происходит за пределами функции memblock_reserve. Если совпадающий - значит, дело в самой функции.


memblock_dbg("memblock_reserve: [%#016llx-%#016llx] %pF, memblock.reserved.total_size=%lu\n",
(unsigned long long)base,
(unsigned long long)base + size,
(void *)_RET_IP_,
(unsigned long) memblock.reserved.total_size);

Смотрим на вывод....


Итак, триумф!

Покажу только две строчки вывода - первую и последнюю. Если кто-то растекался мыслью по чёрно-красному дереву со мной вместе, тот поймёт триумфальность достижения.

[ 0.000000] memblock_reserve: [0x00000001a50000-0x00000001a5d000] setup_arch+0x5e4/0xb80, memblock.reserved.total_size=33673216
...
[ 0.000000] memblock_reserve: [0x0000000fee88c0-0x0000000ff088c0] __alloc_memory_core_early+0x56/0x70, memblock.reserved.total_size=38974946

При этом 0x00000001a5d000-0x00000001a50000 - это всего 53248, а не 33673216 (!!!!!)

Причём вызов идёт из setup_arch, что для нас означает arch/x86/xen или где-то тут, то есть это чисто xen-specific. Или что-то раньше.

Три дня ебли, но я его поймал!
Tags: linux kernel, memory on demand
Subscribe

  • План действий

    AAA при логине ведёт себя по разному в зависимости от того A это или AAAA.

  • Админский гольф

    Вам выдали шелл на сервер, на котором кто-то удалил все симлинки (т.е. файлы типа "симлинк"). Ваша задача починить сервер. Починенным сервер…

  • продолжая leetcode

    Первый раз я ощутил Силу. Задача - roman numerals, с обещанием, что на входе нет мусора. pub fn roman_to_int(s: String) -> i32 { let mut acc =…

  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 0 comments