Почему я не должен использовать ioremap в системной памяти для ARMv6+?
Мне нужно зарезервировать большой буфер физически непрерывной оперативной памяти из ядра и иметь возможность гарантировать, что буфер всегда будет использовать конкретный, жестко закодированный физический адрес. Этот буфер должен оставаться зарезервированным для всего времени жизни ядра. Я написал драйвер chardev в качестве интерфейса для доступа к этому буферу в пользовательском пространстве. Моя платформа - это встроенная система с архитектурой ARMv7, работающая под ядром Linux 2.6.
В главе 15 " Драйверы устройств Linux" третьего издания сказано следующее (стр. 443):
Резервирование верхней части ОЗУ осуществляется путем передачи
mem=
аргумент ядру во время загрузки. Например, если у вас есть 256 МБ, аргументmem=255M
не позволяет ядру использовать верхний мегабайт. Ваш модуль может позже использовать следующий код для получения доступа к такой памяти:dmabuf = ioremap (0xFF00000 /* 255M */, 0x100000 /* 1M */);
Я сделал это плюс несколько других вещей:
- Я использую
memmap
Бутарг в дополнение кmem
один. Документация по параметрам загрузки ядра предлагает всегда использоватьmemmap
всякий раз, когда вы используетеmem
чтобы избежать адресных коллизий. - я использовал
request_mem_region
перед звонкомioremap
и, конечно, я проверяю, что это успешно, прежде чем двигаться вперед.
Вот как выглядит система после того, как я все это сделал:
# cat /proc/cmdline
root=/dev/mtdblock2 console=ttyS0,115200 init=/sbin/preinit earlyprintk debug mem=255M memmap=1M$255M
# cat /proc/iomem
08000000-0fffffff : PCIe Outbound Window, Port 0
08000000-082fffff : PCI Bus 0001:01
08000000-081fffff : 0001:01:00.0
08200000-08207fff : 0001:01:00.0
18000300-18000307 : serial
18000400-18000407 : serial
1800c000-1800cfff : dmu_regs
18012000-18012fff : pcie0
18013000-18013fff : pcie1
18014000-18014fff : pcie2
19000000-19000fff : cru_regs
1e000000-1fffffff : norflash
40000000-47ffffff : PCIe Outbound Window, Port 1
40000000-403fffff : PCI Bus 0002:01
40000000-403fffff : 0002:01:00.0
40400000-409fffff : PCI Bus 0002:01
40400000-407fffff : 0002:01:00.0
40800000-40807fff : 0002:01:00.0
80000000-8fefffff : System RAM
80052000-8045dfff : Kernel text
80478000-80500143 : Kernel data
8ff00000-8fffffff : foo
Пока все выглядит хорошо, и мой водитель работает отлично. Я могу читать и писать напрямую по указанному физическому адресу.
Однако во время загрузки было выдано большое страшное предупреждение (™):
BUG: Your driver calls ioremap() on system memory. This leads
to architecturally unpredictable behaviour on ARMv6+, and ioremap()
will fail in the next kernel release. Please fix your driver.
------------[ cut here ]------------
WARNING: at arch/arm/mm/ioremap.c:211 __arm_ioremap_pfn_caller+0x8c/0x144()
Modules linked in:
[] (unwind_backtrace+0x0/0xf8) from [] (warn_slowpath_common+0x4c/0x64)
[] (warn_slowpath_common+0x4c/0x64) from [] (warn_slowpath_null+0x1c/0x24)
[] (warn_slowpath_null+0x1c/0x24) from [] (__arm_ioremap_pfn_caller+0x8c/0x144)
[] (__arm_ioremap_pfn_caller+0x8c/0x144) from [] (__arm_ioremap_caller+0x50/0x58)
[] (__arm_ioremap_caller+0x50/0x58) from [] (foo_init+0x204/0x2b0)
[] (foo_init+0x204/0x2b0) from [] (do_one_initcall+0x30/0x19c)
[] (do_one_initcall+0x30/0x19c) from [] (kernel_init+0x154/0x218)
[] (kernel_init+0x154/0x218) from [] (kernel_thread_exit+0x0/0x8)
---[ end trace 1a4cab5dbc05c3e7 ]---
Сработало с: дуга / рука / мм / ioremap.c
/*
* Don't allow RAM to be mapped - this causes problems with ARMv6+
*/
if (pfn_valid(pfn)) {
printk(KERN_WARNING "BUG: Your driver calls ioremap() on system memory. This leads\n"
KERN_WARNING "to architecturally unpredictable behaviour on ARMv6+, and ioremap()\n"
KERN_WARNING "will fail in the next kernel release. Please fix your driver.\n");
WARN_ON(1);
}
Какие именно проблемы это может вызвать? Могут ли они быть смягчены? Каковы мои альтернативы?
1 ответ
Так что я сделал именно это, и это работает.
Укажите командную строку ядра (например, / proc / cmdline) и полученную карту памяти (например, / proc / iomem), чтобы проверить это.
Какие именно проблемы это может вызвать?
Проблема с использованием ioremap() в системной памяти заключается в том, что вы в конечном итоге назначаете конфликтующие атрибуты памяти, что вызывает "непредсказуемое" поведение.
См. Статью "Беспорядок в памяти с многократным отображением ARM", в которой содержится история предупреждений, которые вы запускаете.
Ядро ARM отображает RAM как обычную память с кэшированием обратной записи; он также помечен как недопустимый в однопроцессорных системах. Системный вызов ioremap(), используемый для отображения памяти ввода-вывода для использования ЦП, отличается: эта память отображается как память устройства, не кэшируется и, возможно, разделяется. Эти различные сопоставления дают ожидаемое поведение для обоих типов памяти. Ситуация усложняется, когда кто-то вызывает ioremap(), чтобы создать новое отображение для системной памяти.
Проблема с этими множественными сопоставлениями заключается в том, что они будут иметь различные атрибуты. Начиная с версии 6 архитектуры ARM, указанное поведение в этой ситуации "непредсказуемо".
Обратите внимание, что "системная память" - это оперативная память, которой управляет ядро.
Тот факт, что вы запускаете предупреждение, указывает на то, что ваш код генерирует несколько отображений для области памяти.
Могут ли они быть смягчены?
Вы должны убедиться, что оперативная память, которую вы хотите ioremap(), не является "системной памятью", т.е. управляется ядром.
Смотрите также этот ответ.
ДОПОЛНЕНИЕ
Это предупреждение, которое вас беспокоит, является результатом того, что pfn_valid(pfn) возвращает TRUE, а не FALSE.
Основываясь на перекрестной ссылке Linux, которую вы предоставили для версии 2.6.37, pfn_valid () просто возвращает результат
memblock_is_memory(pfn << PAGE_SHIFT);
который в свою очередь просто возвращает результат
memblock_search(&memblock.memory, addr) != -1;
Я предлагаю взломать код ядра, чтобы выявить конфликт.
Перед вызовом ioremap() присвойте TRUE глобальную переменную memblock_debug
,
Следующий патч должен отображать существенную информацию о конфликте памяти.
(Список memblock упорядочен по base-address, поэтому memblock_search() выполняет бинарный поиск в этом списке, поэтому используется mid
в качестве индекса.)
static int __init_memblock memblock_search(struct memblock_type *type, phys_addr_t addr)
{
unsigned int left = 0, right = type->cnt;
do {
unsigned int mid = (right + left) / 2;
if (addr < type->regions[mid].base)
right = mid;
else if (addr >= (type->regions[mid].base +
type->regions[mid].size))
left = mid + 1;
- else
+ else {
+ if (memblock_debug)
+ pr_info("MATCH for 0x%x: m=0x%x b=0x%x s=0x%x\n",
+ addr, mid,
+ type->regions[mid].base,
+ type->regions[mid].size);
return mid;
+ }
} while (left < right);
return -1;
}
Если вы хотите увидеть все блоки памяти, то вызовите memblock_dump_all() с переменной memblock_debug
правда.
[Интересно, что это по сути вопрос программирования, но мы не видели ни одного вашего кода.]
ДОБАВЛЕНИЕ 2
Поскольку вы, вероятно, используете ATAG (вместо дерева устройств) и хотите выделить область памяти, исправьте ATAG_MEM, чтобы отразить этот меньший размер физической памяти.
Предполагая, что вы внесли нулевые изменения в свой загрузочный код, ATAG_MEM по-прежнему задает полный объем ОЗУ, поэтому, возможно, это может быть источником конфликта системной памяти, который вызывает предупреждение.
Посмотрите этот ответ об ATAG и этот связанный ответ.