page_ref_freeze浅析

最近在研究struct page的引用计数refcount,看到有个page_ref_freeze()特性很有意思。用这篇博客记录一下。
本文分析基于linux4.19.195

static inline int page_ref_freeze(struct page *page, int count)
{
	int ret = likely(atomic_cmpxchg(&page->_refcount, count, 0) == count);

	if (page_ref_tracepoint_active(__tracepoint_page_ref_freeze))
		__page_ref_freeze(page, count, ret);
	return ret;
}
static inline void page_ref_unfreeze(struct page *page, int count)
{
	VM_BUG_ON_PAGE(page_count(page) != 0, page);
	VM_BUG_ON(count == 0);

	atomic_set_release(&page->_refcount, count);
	if (page_ref_tracepoint_active(__tracepoint_page_ref_unfreeze))
		__page_ref_unfreeze(page, count);
}

很简单,这两个函数就是page_ref_freeze的所有代码(我确实在开玩笑)。初看实现,无非就是,page_ref_freeze将page->_refcount变成了0,而page_ref_unfreeze将page->_refcount变回了count。

有兴趣的小伙伴,可以看看下面这个提交是怎么用的。

commit e286781d5f2e9c846e012a39653a166e9d31777d
Author: Nick Piggin <npiggin@suse.de>
Date:   Fri Jul 25 19:45:30 2008 -0700

    mm: speculative page references
    
    If we can be sure that elevating the page_count on a pagecache page will
    pin it, we can speculatively run this operation, and subsequently check to
    see if we hit the right page rather than relying on holding a lock or
    otherwise pinning a reference to the page.
    
    This can be done if get_page/put_page behaves consistently throughout the
    whole tree (ie.  if we "get" the page after it has been used for something
    else, we must be able to free it with a put_page).
    
    Actually, there is a period where the count behaves differently: when the
    page is free or if it is a constituent page of a compound page.  We need
    an atomic_inc_not_zero operation to ensure we don't try to grab the page
    in either case.
    
    This patch introduces the core locking protocol to the pagecache (ie.
    adds page_cache_get_speculative, and tweaks some update-side code to make
    it work).
    
    Thanks to Hugh for pointing out an improvement to the algorithm setting
    page_count to zero when we have control of all references, in order to
    hold off speculative getters.
    
    [kamezawa.hiroyu@jp.fujitsu.com: fix migration_entry_wait()]
    [hugh@veritas.com: fix add_to_page_cache]
    [akpm@linux-foundation.org: repair a comment]
    Signed-off-by: Nick Piggin <npiggin@suse.de>
    Cc: Jeff Garzik <jeff@garzik.org>
    Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
    Cc: Paul Mackerras <paulus@samba.org>
    Cc: Hugh Dickins <hugh@veritas.com>
    Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
    Reviewed-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
    Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
    Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
    Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
    Signed-off-by: Hugh Dickins <hugh@veritas.com>
    Acked-by: Nick Piggin <npiggin@suse.de>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

通过第一段注释,我们就能明白page_ref_freeze的作用了。

If we can be sure that elevating the page_count on a pagecache page will
pin it, we can speculatively run this operation, and subsequently check to
see if we hit the right page rather than relying on holding a lock or
otherwise pinning a reference to the page.

结合代码,在释放一个address_space引用的pagecache的时候,我们可以通过page_ref_freeze()函数,将一个页的引用计数清零,从而在改页的整个释放过程中,我们可以完全忽略引用计数的检查。简单来说,就是通过page_ref_freeze(),我们就知道我们能不能去释放这个pagecache了:
如果引用计数并非page_ref_freeze()传入的预期的值,就不能释放该page(因为有可能有其他模块pin住了该page),这个很好理解。
如果是预期的值,那就把引用计数改为0,让后放心大胆的去做释放pagecacahe的动作。这个是为什么?如果有人在我们把引用计数改为0之后,又去get_page(),表示又有人想pin住pagecache,岂不是要出问题?
通过查看提交,我们可以看到,作者也想到了这种情况,因而他遍历了内核里的所有代码,将有可能对pagecache进行get_page()的地方,全部改成了page_cache_get_speculative(),这个函数实际上就是包装了函数get_page_unless_zero(page)。这里大家就能明白了,就是说,对于pagecache这个类型的page,我们如果想使用他,必须调用page_cache_get_speculative()去pin住他,而不能通过get_page()去pin住他。这样做的话,对于pagecache的引用计数,就只会有三种操作了:
一种是page_cache_get_speculative(),也就是get_page_unless_zero(page)
一种是page_ref_freeze()
一种是put_page()
有了get_page_unless_zero(page)的保障,page_ref_freeze()的逻辑就完备了,因为如果page_ref_freeze()成功,那肯定能杜绝后续对这个页的pin的动作,而如果page_cache_get_speculative()成功,那page_ref_freeze()就绝不可能成功。嗯,确实挺绕的。当然,这也就是page_ref_freeze这个功能的局限性,仅能用在小部分场景,并不像普通的get_page()/put_page()那样普遍适用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值