1 固定映射 1.1 數據結構 linux高端記憶體中的臨時記憶體區為固定記憶體區的一部分, 對於固定記憶體在linux內核中有下麵描述 x86| arm| arm64 | | "arch/x86/include/asm/fixmap.h?v=4.7, line 67" | "arch/arm/includ ...
1 固定映射
1.1 數據結構
linux高端記憶體中的臨時記憶體區為固定記憶體區的一部分, 對於固定記憶體在linux內核中有下麵描述
x86 | arm | arm64 |
---|---|---|
arch/x86/include/asm/fixmap.h?v=4.7, line 67 | arch/arm/include/asm/fixmap.h?v=4.7, line 11 | arch/arm64/include/asm/fixmap.h?v=4.7, line 36 |
/*
* Here we define all the compile-time 'special' virtual
* addresses. The point is to have a constant address at
* compile time, but to set the physical address only
* in the boot process.
*
* These 'compile-time allocated' memory buffers are
* page-sized. Use set_fixmap(idx,phys) to associate
* physical memory with fixmap indices.
*
*/
enum fixed_addresses {
FIX_HOLE,
/*
* Reserve a virtual window for the FDT that is 2 MB larger than the
* maximum supported size, and put it at the top of the fixmap region.
* The additional space ensures that any FDT that does not exceed
* MAX_FDT_SIZE can be mapped regardless of whether it crosses any
* 2 MB alignment boundaries.
*
* Keep this at the top so it remains 2 MB aligned.
*/
#define FIX_FDT_SIZE (MAX_FDT_SIZE + SZ_2M)
FIX_FDT_END,
FIX_FDT = FIX_FDT_END + FIX_FDT_SIZE / PAGE_SIZE - 1,
FIX_EARLYCON_MEM_BASE,
FIX_TEXT_POKE0,
__end_of_permanent_fixed_addresses,
/*
* Temporary boot-time mappings, used by early_ioremap(),
* before ioremap() is functional.
*/
#define NR_FIX_BTMAPS (SZ_256K / PAGE_SIZE)
#define FIX_BTMAPS_SLOTS 7
#define TOTAL_FIX_BTMAPS (NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS)
FIX_BTMAP_END = __end_of_permanent_fixed_addresses,
FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1,
/*
* Used for kernel page table creation, so unmapped memory may be used
* for tables.
*/
FIX_PTE,
FIX_PMD,
FIX_PUD,
FIX_PGD,
__end_of_fixed_addresses
};
1.2 固定映射
ioremap
的作用是將IO
和BIOS
以及物理地址空間映射到在896M至1G的128M的地址空間內, 使得kernel能夠訪問該空間併進行相應的讀寫操作。
start_kernel()->setup_arch()->early_ioremap_init()
然後arm和arm64上early_ioremap_init
又是early_ioremap_setup
的前端
函數 | x86 | arm | arm64 |
---|---|---|---|
early_ioremap_init | arch/x86/mm/ioremap.c?v=4.7, line 445 | arch/arm/mm/ioremap.c?v=4.7, line 489 | arch/arm64/mm/ioremap.c?v=4.7, line 110 |
early_ioremap_setup | mm/early_ioremap.c?v=4.7, line 67 | 體繫結構無關 | 體繫結構無關 |
/*
* Must be called after early_fixmap_init
*/
void __init early_ioremap_init(void)
{
early_ioremap_setup();
}
但是arm和arm64下的setup_arch函數則會先調用early_fixmap_init
函數來填充fixmap
. 參見arch/arm/kernel/setup.c?v=4.7, line 1058和arch/arm64/kernel/setup.c?v=4.7, line 229.
void __init setup_arch(char **cmdline_p)
{
early_fixmap_init();
early_ioremap_init();
}
early_fixmap_init
函數的定義在
arm | arm64 |
---|---|
arch/arm/mm/mmu.c?v=4.7, line 385 | arch/arm64/mm/mmu.c?v=4.7, line 676 |
其中arm架構的定義如下所示, 在arch/arm/mm/mmu.c?v=4.7, line 385
void __init early_fixmap_init(void)
{
pmd_t *pmd;
/*
* The early fixmap range spans multiple pmds, for which
* we are not prepared:
*/
BUILD_BUG_ON((__fix_to_virt(__end_of_early_ioremap_region) >> PMD_SHIFT)
!= FIXADDR_TOP >> PMD_SHIFT);
/*得到固定映射區的pmd
,此pmd為虛擬地址轉換為物理地址的pmd*/
pmd = fixmap_pmd(FIXADDR_TOP);
/*將bm_pte頁表設置為固定映射區開始地址的pmd的第一個頁表;*/
pmd_populate_kernel(&init_mm, pmd, bm_pte);
pte_offset_fixmap = pte_offset_early_fixmap;
}
1.3 ioremap函數
對於ioremap
的使用需要通過early_memremap
和early_iounmap
進行.
由於對應於ioremap的記憶體空間是有限的, 所以對於ioremap空間的使用遵照使用結束馬上釋放的原則. 這就是說early_memremap和early_iounmap必須配對使用並且訪問結束必須馬上執行unmap
2 臨時內核映射
剛纔描述的kmap
函數不能用於中斷處理程式, 因為它可能進入睡眠狀態. 如果pkmap數組中沒有空閑位置, 該函數會進入睡眠狀態, 直至情形有所改善.
因此內核提供了一個備選的映射函數, 其執行是原子的, 邏輯上稱為kmap_atomic. 該函數的一個主要優點是它比普通的kmap快速. 但它不能用於可能進入睡眠的代碼. 因此, 它對於很快就需要一個臨時頁的簡短代碼,是非常理想的.
kmap_atomic的定義在IA-32, PPC, Sparc32上是特定於體繫結構的, 但這3種實現只有非常細微的差別. 其原型是相同的.
2.1 kmap_atomic函數
// http://lxr.free-electrons.com/source/arch/arm/mm/highmem.c?v=4.7#L55
void *kmap_atomic(struct page *page)
page是一個指向高端記憶體頁的管理結構的指針, 而早期的內核中, 增加了一個類型為enum km_type的type參數, 用於指定所需的映射類型
// http://lxr.free-electrons.com/source/arch/arm/mm/highmem.c?v=2.6.32#L39
void *kmap_atomic(struct page *page, enum km_type type)
而在新的內核中, 刪除了這個標識, 但是保留了km_type的最大值KM_TYPE_NR
void *kmap_atomic(struct page *page)
{
unsigned int idx;
unsigned long vaddr;
void *kmap;
int type;
preempt_disable();
pagefault_disable();
if (!PageHighMem(page))
return page_address(page);
#ifdef CONFIG_DEBUG_HIGHMEM
/*
* There is no cache coherency issue when non VIVT, so force the
* dedicated kmap usage for better debugging purposes in that case.
*/
if (!cache_is_vivt())
kmap = NULL;
else
#endif
kmap = kmap_high_get(page);
if (kmap)
return kmap;
type = kmap_atomic_idx_push();
idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id();
vaddr = __fix_to_virt(idx);
#ifdef CONFIG_DEBUG_HIGHMEM
/*
* With debugging enabled, kunmap_atomic forces that entry to 0.
* Make sure it was indeed properly unmapped.
*/
BUG_ON(!pte_none(get_fixmap_pte(vaddr)));
#endif
/*
* When debugging is off, kunmap_atomic leaves the previous mapping
* in place, so the contained TLB flush ensures the TLB is updated
* with the new mapping.
*/
set_fixmap_pte(idx, mk_pte(page, kmap_prot));
return (void *)vaddr;
}
EXPORT_SYMBOL(kmap_atomic);
這個函數不會被阻塞, 因此可以用在中斷上下文和起亞不能重新調度的地方. 它也禁止內核搶占, 這是有必要的, 因此映射對每個處理器都是唯一的(調度可能對哪個處理器執行哪個進程做變動).
2.2 kunmap_atomic函數
可以通過函數kunmap_atomic取消映射
/*
* Prevent people trying to call kunmap_atomic() as if it were kunmap()
* kunmap_atomic() should get the return value of kmap_atomic, not the page.
*/
#define kunmap_atomic(addr) \
do { \
BUILD_BUG_ON(__same_type((addr), struct page *)); \
__kunmap_atomic(addr); \
} while (0)
這個函數也不會阻塞. 在很多體繫結構中, 除非激活了內核搶占, 否則kunmap_atomic根本無事可做, 因為只有在下一個臨時映射到來前上一個臨時映射才有效. 因此, 內核完全可以”忘掉”kmap_atomic映射, kunmap_atomic也無需做什麼實際的事情. 下一個原子映射將自動覆蓋前一個映射.