Chinaunix

标题: 更改 page table entry屬性,卻未生效 [打印本页]

作者: wth0722    时间: 2014-06-06 22:31
标题: 更改 page table entry屬性,卻未生效
本帖最后由 wth0722 于 2014-06-06 22:39 编辑

各位先進好,

小弟用ioremap()要了一塊記憶體使用

ioreamp()初始化是要了一塊non-cache的記憶體

我後來有去找該address所在的page table entry

找到 ptep = pte_offset_map(pmd, addr);

把ptep的prot改為 PROT_PTE_DEVICE | L_PTE_MT_DEV_CACHED

上面即為ioremap_cached的屬性

改完之後,以為那一塊記憶體就有cache了,但還是沒有

請教各位大大有什麼想法嗎? 謝謝


作者: wth0722    时间: 2014-06-06 22:42
本帖最后由 wth0722 于 2014-06-06 22:43 编辑

還是我需要用下列這方式

set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));

去set那page table entry的屬性才是真正寫入成功???




作者: humjb_1983    时间: 2014-06-07 13:13
怎么确认没成功的呢?
作者: wth0722    时间: 2014-06-07 14:46
我有一個測試程式利用dma
不去做flush cache就可以看到不一樣結果
作者: humjb_1983    时间: 2014-06-09 09:04
wth0722 发表于 2014-06-07 14:46
我有一個測試程式利用dma
不去做flush cache就可以看到不一樣結果

修改后,再把相应pte的内容打出来看看呢~
作者: wth0722    时间: 2014-06-09 13:19
已經找出原因了

單純只是去修改記憶體上的page table並沒有用

必需要使用set_pte_at()設定page table entry才有用
作者: wth0722    时间: 2014-06-09 15:13
看了一下 set_pte_at()之後

發現為什麼直接改memory上的page table entry是無效了

因為set_pte_at()有去變更h/w pte,而上面的變更只是linux pte
作者: humjb_1983    时间: 2014-06-09 15:48
你的内核是啥版本?
作者: wth0722    时间: 2014-06-09 16:49
Linux 3.4.5
作者: humjb_1983    时间: 2014-06-09 18:32
我看3.10代码中么有set_pte_at()接口的定义。
作者: l4rmbr    时间: 2014-06-09 22:42
本帖最后由 l4rmbr 于 2014-06-09 22:54 编辑

看了楼主的表述,楼主你应该是在更新页表项后忘了冲刷tlb缓存。正确用法应该像这样:

flush_cache_page(vma, addr, pfn);             <--- 冲刷数据/指令缓存
set_pte(pte_pointer, new_pte_val);             <--- 更新页表项
flush_tlb_page(vma, addr);                          <--- 冲刷tlb缓存

此外,多说两点.

1.
set_pte_at() 与 set_pte()功能基本相同,不过前者用来实现钩子,用以
冲刷相应的数据/指令缓存。

在大多数体系架构下,这两个函数一样。不过arm是个例外。因为它是vivt
架构(virtual indexed and virtual tagged), 所以更新了虚拟地址的页表项,
也要相应地刷新缓存。 x86就不用,因为它是pipi(physical indexed and physical tagged)
缓存一致性由cpu内在地保证,不需要程序员干预。

2.
我大概翻了下代码,发现set_pte一般用在第一次创建一个表项时,此时无所谓“更新”一说。
如果是要更新一个旧页表项,则用的是set_pte_at。
这也符合这两个接口的设计目的。

作者: l4rmbr    时间: 2014-06-09 22:43
本帖最后由 l4rmbr 于 2014-06-09 22:54 编辑

[非常抱歉,网络原因发了相同内容两次,麻烦版主删除这个帖子吧。谢谢!]

看了楼主的表述,楼主你应该是在更新页表项后忘了冲刷tlb缓存。正确用法应该像这样:

flush_cache_page(vma, addr, pfn);             <--- 冲刷数据/指令缓存
set_pte(pte_pointer, new_pte_val);             <--- 更新页表项
flush_tlb_page(vma, addr);                          <--- 冲刷tlb缓存

此外,多说两点.

1.
set_pte_at() 与 set_pte()功能基本相同,不过前者用来实现钩子,用以
冲刷相应的数据/指令缓存。

在大多数体系架构下,这两个函数一样。不过arm是个例外。因为它是vivt
架构(virtual indexed and virtual tagged), 所以更新了虚拟地址的页表项,
也要相应地刷新缓存。 x86就不用,因为它是pipi(physical indexed and physical tagged)
缓存一致性由cpu内在地保证,不需要程序员干预。

2.
我大概翻了下代码,发现set_pte一般用在第一次创建一个表项时,此时无所谓“更新”一说。
如果是要更新一个旧页表项,则用的是set_pte_at。
这也符合这两个接口的设计目的。
作者: wth0722    时间: 2014-06-10 10:21
回复 12# l4rmbr

先感謝你的回應

flush_cache_page(vma, addr, pfn);             <--- 沖刷數據/指令緩存
set_pte(pte_pointer, new_pte_val);             <--- 更新頁表項
flush_tlb_page(vma, addr);                          <--- 沖刷tlb緩存

我無法用上面那些,因為ioremap()後,那塊記憶體是沒有vma的
我試過find_vma()是找不到的


我這邊是ARM arch,並沒有set_pte(),只有set_pte_at()
再來其實一開始我是有flush all cache 以及 鎖TLB,但還是沒有解決問題
後來才知道是因為我是只有更改Linux pte,並沒有改道h/w pte
所以才會需要set_pte_at()幫我去修改他


還有其實ARM arch的d-cache 是VIPT non-aliasing
i-cache才是VIVT


   
作者: chenyu105    时间: 2014-06-10 11:33
奇怪,我看的内核是3.13,如果照楼主的流程,用set_pte_at并不会刷cache。

对于set_pte_at,如果是用户态的地址,则尝试刷cache:
  1. static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
  2.                               pte_t *ptep, pte_t pteval)
  3. {
  4.         unsigned long ext = 0;

  5.         if (addr < TASK_SIZE && pte_present_user(pteval)) {
  6.                 __sync_icache_dcache(pteval);
  7.                 ext |= PTE_EXT_NG;
  8.         }

  9. }
复制代码
如果是用户态的地址,并且有可执行属性,就算vipt 没有cache aliase,也需刷icache和dcache:

  1. void __sync_icache_dcache(pte_t pteval)
  2. {
  3.         unsigned long pfn;
  4.         struct page *page;
  5.         struct address_space *mapping;

  6.         if (cache_is_vipt_nonaliasing() && !pte_exec(pteval))
  7.                 /* only flush non-aliasing VIPT caches for exec mappings */
  8.                 return;
复制代码

  1. #define pte_exec(pte)                (!(pte_val(pte) & L_PTE_XN))
复制代码
由于楼主的ioremap带PROT_PTE_DEVICE属性,
#define PROT_PTE_DEVICE                L_PTE_PRESENT|L_PTE_YOUNG|L_PTE_DIRTY|L_PTE_XN
因此pte_exec返回0,从而__sync_icache_dcache直接返回了。

也就是说,不会刷cache,这个跟楼主的描述有点矛盾。

作者: l4rmbr    时间: 2014-06-10 11:47
回复 13# wth0722


    你好。我看了下ioremap的实现,确实是没关联vma的。

    还有,你说的hw pte是怎么回事? 页表项不就是在内存中的数据结构吗?还有硬件页表项这一说?不知是什么架构。能不能解释下?谢谢!

    最后,关于vivt, 多谢指正 :)
作者: wth0722    时间: 2014-06-10 11:51
回复 14# chenyu105

不好意思,請問跟我講哪一點有矛盾?

set_pte_at() -> cpu_v7_set_pte_ext() 這邊若是UP則會flush cache

我從頭到尾就一直說問題的點是在於只有寫入到Linux pte,並沒有寫到h/w pte

TLB、cache的問題我不認為問題點在那邊阿,因為我試過flush也沒用
   
作者: wth0722    时间: 2014-06-10 11:58
回复 15# l4rmbr


我用是ARM arch的,這邊是有兩份pte的

原因是Linux所要的pte資訊,ARM並沒有提供,所以要多一份保存

請參考arch/arm/include/asm/pgtable-2level.h

31  * This leads to the page tables having the following layout:
32  *
33  *    pgd             pte
34  * |        |
35  * +--------+
36  * |        |                 +------------+ +0
37  * +- - - - +               | Linux pt 0 |
38  * |        |                 +------------+ +1024
39  * +--------+     +0    | Linux pt 1 |
40  * |        |----->       +------------+ +2048
41  * +- - - - + +4          |  h/w pt 0  |
42  * |        |----->       +------------+ +3072
43  * +--------+     +8    |  h/w pt 1  |
44  * |        |                +------------+ +4096
45  *

作者: l4rmbr    时间: 2014-06-10 12:06
回复 17# wth0722


    多谢。明白了。 这也是为了弥补linux通用多级页表和硬件支持页表之间的差异。
作者: chenyu105    时间: 2014-06-10 12:17
本帖最后由 chenyu105 于 2014-06-10 12:20 编辑

回复 16# wth0722
明白了。
如果设置了软件pte,应该也会有什么时候会把软件pte更新到硬件pte吧
   
作者: wth0722    时间: 2014-06-10 13:08
回复 19# chenyu105


set_pte_at()  --->  cpu_v7_set_pte_ext()

cpu_v7_set_pte_ext()這裡面會去寫入到h/w pte


   




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2