Портирование модуля на более новое ядро Linux: невозможно выделить память
У меня довольно большой модуль драйвера, который я пытаюсь скомпилировать для недавнего ядра Linux (3.4.4). Я могу успешно скомпилировать и insmod
тот же модуль с ядром 2.6.27.25. Версия GCC также отличается, 4.7.0 против 4.3.0. Обратите внимание, что этот модуль довольно сложный, и я не могу просто просмотреть весь код и все make-файлы.
При "вставке" модуля я получаю Cannot allocate memory
со следующими следами:
vmap allocation for size 30248960 failed: use vmalloc=<size> to increase size.
vmalloc: allocation failure: 30243566 bytes
insmod: page allocation failure: order:0, mode:0xd2
Pid: 5840, comm: insmod Tainted: G O 3.4.4-5.fc17.i686 #1
Call Trace:
[<c092702a>] ? printk+0x2d/0x2f
[<c04eff8d>] warn_alloc_failed+0xad/0xf0
[<c05178d9>] __vmalloc_node_range+0x169/0x1d0
[<c0517994>] __vmalloc_node+0x54/0x60
[<c0490825>] ? sys_init_module+0x65/0x1d80
[<c0517a60>] vmalloc+0x30/0x40
[<c0490825>] ? sys_init_module+0x65/0x1d80
[<c0490825>] sys_init_module+0x65/0x1d80
[<c050cda6>] ? handle_mm_fault+0xf6/0x1d0
[<c0932b30>] ? spurious_fault+0xae/0xae
[<c0932ce7>] ? do_page_fault+0x1b7/0x450
[<c093665f>] sysenter_do_call+0x12/0x28
-- clip --
Очевидный ответ, по-видимому, заключается в том, что модуль выделяет слишком много памяти:
- У меня нет проблем со старой версией ядра, какого размера этот модуль
- если я отрежу какую-то часть этого модуля, чтобы получить намного меньшее потребление памяти, я получу всегда одно и то же сообщение об ошибке с новым ядром
- Я могу выгрузить много других модулей, но это не имеет никакого влияния (и так ли это в любом случае? Есть ли в Linux глобальный лимит в отношении общего использования памяти модулями)
Поэтому я подозреваю, что проблема с новым ядром не связана напрямую с ограниченным объемом памяти.
Новое ядро жалуется на vmalloc()
из 30000 КБ, но со старым ядром lsmod дает мне размер 4800 КБ. Должны ли эти цифры быть непосредственно связаны? Возможно ли, что во время сборки что-то пошло не так и запрошено слишком много оперативной памяти? Когда я компилирую размер разделов обоих .ko
Я не вижу большой разницы.
Поэтому я пытаюсь понять, откуда проблема. Когда я проверяю выгруженный стек, я не могу найти соответствующий фрагмент кода. Кажется, что неисправный vmalloc()
сделано sys_init_module()
, который init_module()
от kernel/module.c
, Но код не совпадает. Когда я проверяю код объекта из моего .ko
, init_module()
Код также не совпадает.
Я более или менее заблокирован, так как недостаточно хорошо знаю ядро, и всю систему сборки и загрузку модулей довольно сложно понять. Ошибка возникает до загрузки модуля, так как я подозреваю, что некоторые функции отсутствуют и insmod
не сообщает об этих ошибках на данный момент.
2 ответа
Я считаю, что распределение сделано в layout_and_allocate
который называется load_module
, Оба являются статическими функциями, поэтому они могут быть встроенными и, следовательно, не в стеке.
Так что это не распределение, сделанное вашим кодом, а выделение, сделанное Linux для загрузки вашего кода.
Если ваше старое ядро имеет размер 4,8 МБ, а новое - 30 МБ, это может объяснить причину сбоя.
Так что вопрос в том, почему он такой большой.
Размер может быть связан с объемом кода (маловероятно, что он так сильно вырос) или статически распределенными данными.
Вероятное объяснение состоит в том, что у вас есть большой статически распределенный массив, размер которого определен в Linux. Если размер значительно увеличился, ваш массив будет расти.
Догадка - массив, размер которого NR_CPUS
,
Вы должны быть в состоянии использовать такие команды, как nm
или же objdump
найти такой массив. Однако я не уверен, как именно это сделать.
Проблема была на самом деле из-за разделов отладки в модуле. Старое ядро могло игнорировать эти разделы, но новое подсчитывало их в общем размере для выделения. Однако при включении pr_debug()
трассировки из module.c во время загрузки, эти разделы не были сброшены с другими.
Как от них избавиться и решить проблему:
objcopy -R .debug_aranges \
-R .debug_info \
-R .debug_abbrev \
-R .debug_line \
-R .debug_frame \
-R .debug_str \
-R .debug_loc \
-R .debug_ranges \
orignal.ko new.ko
Также возможно, что в конкретные файлы сборки для этого проекта добавлялась отладочная информация, "приспособленная" для старой версии ядра, но при попытке с фиктивным модулем я нашел точно такой же добавленный раздел отладки, так что я бы скорее заподозрил некоторые изменение политики в отношении управления модулями в ядре или в Fedora.
Любая информация об этих изменениях приветствуется.