[bug report]: high memory and cache alias handing

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I found some issue about cache alias when trying to support 4KB page 
on Loongson 3A1000 (VIPT L1D and L1C, both 4-way in total 64KB)

1.  __flush_anon_page 
.......
void __flush_anon_page(struct page *page, unsigned long vmaddr)
{
	//A:if page is in high memory, page_address does not return
	//meaningful addr
	unsigned long addr = (unsigned long) page_address(page);

	if (pages_do_alias(addr, vmaddr)) {
		if (page_mapcount(page) && !Page_dcache_dirty(page)) {
			void *kaddr;

			kaddr = kmap_coherent(page, vmaddr);
			flush_data_cache_page((unsigned long)kaddr);
			kunmap_coherent();
		} else
			//A: so can not use this addr for cache flush

			//for 64bit kernel, although addr returned is OK
			//but the flush_data_cache_page(addr) DOES NOT
			//touch the cache alias
			flush_data_cache_page(addr);
	}
}
........

to fix __flush_anon_page, I has one question

+. Why using kmap_coherent depends on !Page_dcache_dirty(page)?

I think after ce01948eb85d and 0f334a3e8c35, it is safe to
kmap_coherent without checking Page_dcache_dirty(page)

since with preempt disabled in kmap_coherent, and in interrupt use
indepent fixmap index, there is no collision in TLB  

void __flush_anon_page(struct page *page, unsigned long vmaddr)
{
	void *kaddr;
	if (page_mapcount(page)) {

		kaddr = kmap_coherent(page, vmaddr);
		flush_data_cache_page((unsigned long)kaddr);
		kunmap_coherent();
	}
}

2.  __flush_dcache_page 
........
void __flush_dcache_page(struct page *page)
{
	struct address_space *mapping = page_mapping_file(page);
	unsigned long addr;

	if (mapping && !mapping_mapped(mapping)) {
		SetPageDcacheDirty(page);
		return;
	}

	/*
	 * We could delay the flush for the !page_mapping case too.  But that
	 * case is for exec env/arg pages and those are %99 certainly going to
	 * get faulted into the tlb (and thus flushed) anyways.
	 */
	//B: calling __flush_dcache_page is under cpu_has_dc_aliases, so
	//we can not make sure addr returned by kmap_atomic is some
	//color with page's physical color, 
	//how can flush_data_cache_page flush cache line with this page
	if (PageHighMem(page))
		addr = (unsigned long)kmap_atomic(page);
	else
		addr = (unsigned long)page_address(page);

	flush_data_cache_page(addr);

	if (PageHighMem(page))
		kunmap_atomic((void *)addr);
}
........
3.  __update_tlb
.....
void __update_cache(unsigned long address, pte_t pte)
{
	struct page *page;
	unsigned long pfn, addr;
	int exec = !pte_no_exec(pte) && !cpu_has_ic_fills_f_dc;

	pfn = pte_pfn(pte);
	if (unlikely(!pfn_valid(pfn)))
		return;
	page = pfn_to_page(pfn);
	if (Page_dcache_dirty(page)) {
		//C: same as B, can not make sure the addr is in
		//same color with address
		if (PageHighMem(page))
			addr = (unsigned long)kmap_atomic(page);
		else
			addr = (unsigned long)page_address(page);
		//C: if exec is true and addr is NOT in same color with address
		//then flush_data_cache_page CAN NOT hit writeback and
		//invalidate page's cache line

		//C: if exec is false, page_do_alias return true, AKA
		addr is not in same color with address
		//then flush_data_cache_page(addr) 
		if (exec || pages_do_alias(addr, address & PAGE_MASK))
			flush_data_cache_page(addr);

		if (PageHighMem(page))
			kunmap_atomic((void *)addr);

		ClearPageDcacheDirty(page);
	}
}


all 3 issues need page_coherent to work without restriction of
!Page_Dcache_Dirty()




[Index of Archives]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux