Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753214AbcC0Trz (ORCPT ); Sun, 27 Mar 2016 15:47:55 -0400 Received: from mga04.intel.com ([192.55.52.120]:41985 "EHLO mga04.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753053AbcC0Trw (ORCPT ); Sun, 27 Mar 2016 15:47:52 -0400 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.24,402,1455004800"; d="scan'208";a="946020384" From: "Kirill A. Shutemov" To: Eric Biggers Cc: Hugh Dickins , linux-kernel@vger.kernel.org, linux-mm@kvack.org, "Kirill A. Shutemov" Subject: [PATCH 1/4] page-flags: generate page-flags helpers with script Date: Sun, 27 Mar 2016 22:47:37 +0300 Message-Id: <1459108060-69891-1-git-send-email-kirill.shutemov@linux.intel.com> X-Mailer: git-send-email 2.8.0.rc3 In-Reply-To: <20160327194649.GA9638@node.shutemov.name> References: <20160327194649.GA9638@node.shutemov.name> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 18393 Lines: 623 scripts/mkpageflags.sh generates include/generated/page-flags.h using directive in mm/page-flags.tlb. Format of the mm/page-flags.tlb: lines not started with capital letter, just copied. Lines which starts with capital letter are rules to generate page-flags helper: Result of generation is set of helper macros, not functions. It's required for future extension. Type safety is controlled with __builtin_types_compatible_p() check. Not-yet-signed-off-by: Kirill A. Shutemov --- Kbuild | 30 +++++-- include/linux/page-flags.h | 217 +-------------------------------------------- mm/page-flags.tbl | 102 +++++++++++++++++++++ scripts/mkpageflags.sh | 151 +++++++++++++++++++++++++++++++ 4 files changed, 279 insertions(+), 221 deletions(-) create mode 100644 mm/page-flags.tbl create mode 100755 scripts/mkpageflags.sh diff --git a/Kbuild b/Kbuild index f55cefd9bf29..405c7145af95 100644 --- a/Kbuild +++ b/Kbuild @@ -3,8 +3,9 @@ # This file takes care of the following: # 1) Generate bounds.h # 2) Generate timeconst.h -# 3) Generate asm-offsets.h (may need bounds.h and timeconst.h) -# 4) Check for missing system calls +# 3) Generate page-flags.h +# 4) Generate asm-offsets.h (may need bounds.h, timeconst.h and page-flags.h) +# 5) Check for missing system calls # Default sed regexp - multiline due to syntax constraints define sed-y @@ -66,7 +67,25 @@ $(obj)/$(timeconst-file): kernel/time/timeconst.bc FORCE $(call filechk,gentimeconst) ##### -# 3) Generate asm-offsets.h +# 3) Generate page-flags.h + +pageflags-file := include/generated/page-flags.h + +targets += $(pageflags-file) + +quiet_cmd_genpageflags = GEN $@ +define cmd_genpageflags + $(srctree)/scripts/mkpageflags.sh <$< >$@ +endef +define filechk_genpageflags + $(srctree)/scripts/mkpageflags.sh <$< +endef + +$(obj)/$(pageflags-file): mm/page-flags.tbl FORCE + $(call filechk,genpageflags) + +##### +# 4) Generate asm-offsets.h # offsets-file := include/generated/asm-offsets.h @@ -76,7 +95,8 @@ targets += arch/$(SRCARCH)/kernel/asm-offsets.s # We use internal kbuild rules to avoid the "is up to date" message from make arch/$(SRCARCH)/kernel/asm-offsets.s: arch/$(SRCARCH)/kernel/asm-offsets.c \ - $(obj)/$(timeconst-file) $(obj)/$(bounds-file) FORCE + $(obj)/$(timeconst-file) $(obj)/$(bounds-file) \ + $(obj)/$(pageflags-file) FORCE $(Q)mkdir -p $(dir $@) $(call if_changed_dep,cc_s_c) @@ -84,7 +104,7 @@ $(obj)/$(offsets-file): arch/$(SRCARCH)/kernel/asm-offsets.s FORCE $(call filechk,offsets,__ASM_OFFSETS_H__) ##### -# 4) Check for missing system calls +# 5) Check for missing system calls # always += missing-syscalls diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index f4ed4f1b0c77..d111caad2a22 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -154,202 +154,7 @@ static __always_inline int PageCompound(struct page *page) return test_bit(PG_head, &page->flags) || PageTail(page); } -/* - * Page flags policies wrt compound pages - * - * PF_ANY: - * the page flag is relevant for small, head and tail pages. - * - * PF_HEAD: - * for compound page all operations related to the page flag applied to - * head page. - * - * PF_NO_TAIL: - * modifications of the page flag must be done on small or head pages, - * checks can be done on tail pages too. - * - * PF_NO_COMPOUND: - * the page flag is not relevant for compound pages. - */ -#define PF_ANY(page, enforce) page -#define PF_HEAD(page, enforce) compound_head(page) -#define PF_NO_TAIL(page, enforce) ({ \ - VM_BUG_ON_PGFLAGS(enforce && PageTail(page), page); \ - compound_head(page);}) -#define PF_NO_COMPOUND(page, enforce) ({ \ - VM_BUG_ON_PGFLAGS(enforce && PageCompound(page), page); \ - page;}) - -/* - * Macros to create function definitions for page flags - */ -#define TESTPAGEFLAG(uname, lname, policy) \ -static __always_inline int Page##uname(struct page *page) \ - { return test_bit(PG_##lname, &policy(page, 0)->flags); } - -#define SETPAGEFLAG(uname, lname, policy) \ -static __always_inline void SetPage##uname(struct page *page) \ - { set_bit(PG_##lname, &policy(page, 1)->flags); } - -#define CLEARPAGEFLAG(uname, lname, policy) \ -static __always_inline void ClearPage##uname(struct page *page) \ - { clear_bit(PG_##lname, &policy(page, 1)->flags); } - -#define __SETPAGEFLAG(uname, lname, policy) \ -static __always_inline void __SetPage##uname(struct page *page) \ - { __set_bit(PG_##lname, &policy(page, 1)->flags); } - -#define __CLEARPAGEFLAG(uname, lname, policy) \ -static __always_inline void __ClearPage##uname(struct page *page) \ - { __clear_bit(PG_##lname, &policy(page, 1)->flags); } - -#define TESTSETFLAG(uname, lname, policy) \ -static __always_inline int TestSetPage##uname(struct page *page) \ - { return test_and_set_bit(PG_##lname, &policy(page, 1)->flags); } - -#define TESTCLEARFLAG(uname, lname, policy) \ -static __always_inline int TestClearPage##uname(struct page *page) \ - { return test_and_clear_bit(PG_##lname, &policy(page, 1)->flags); } - -#define PAGEFLAG(uname, lname, policy) \ - TESTPAGEFLAG(uname, lname, policy) \ - SETPAGEFLAG(uname, lname, policy) \ - CLEARPAGEFLAG(uname, lname, policy) - -#define __PAGEFLAG(uname, lname, policy) \ - TESTPAGEFLAG(uname, lname, policy) \ - __SETPAGEFLAG(uname, lname, policy) \ - __CLEARPAGEFLAG(uname, lname, policy) - -#define TESTSCFLAG(uname, lname, policy) \ - TESTSETFLAG(uname, lname, policy) \ - TESTCLEARFLAG(uname, lname, policy) - -#define TESTPAGEFLAG_FALSE(uname) \ -static inline int Page##uname(const struct page *page) { return 0; } - -#define SETPAGEFLAG_NOOP(uname) \ -static inline void SetPage##uname(struct page *page) { } - -#define CLEARPAGEFLAG_NOOP(uname) \ -static inline void ClearPage##uname(struct page *page) { } - -#define __CLEARPAGEFLAG_NOOP(uname) \ -static inline void __ClearPage##uname(struct page *page) { } - -#define TESTSETFLAG_FALSE(uname) \ -static inline int TestSetPage##uname(struct page *page) { return 0; } - -#define TESTCLEARFLAG_FALSE(uname) \ -static inline int TestClearPage##uname(struct page *page) { return 0; } - -#define PAGEFLAG_FALSE(uname) TESTPAGEFLAG_FALSE(uname) \ - SETPAGEFLAG_NOOP(uname) CLEARPAGEFLAG_NOOP(uname) - -#define TESTSCFLAG_FALSE(uname) \ - TESTSETFLAG_FALSE(uname) TESTCLEARFLAG_FALSE(uname) - -__PAGEFLAG(Locked, locked, PF_NO_TAIL) -PAGEFLAG(Error, error, PF_NO_COMPOUND) TESTCLEARFLAG(Error, error, PF_NO_COMPOUND) -PAGEFLAG(Referenced, referenced, PF_HEAD) - TESTCLEARFLAG(Referenced, referenced, PF_HEAD) - __SETPAGEFLAG(Referenced, referenced, PF_HEAD) -PAGEFLAG(Dirty, dirty, PF_HEAD) TESTSCFLAG(Dirty, dirty, PF_HEAD) - __CLEARPAGEFLAG(Dirty, dirty, PF_HEAD) -PAGEFLAG(LRU, lru, PF_HEAD) __CLEARPAGEFLAG(LRU, lru, PF_HEAD) -PAGEFLAG(Active, active, PF_HEAD) __CLEARPAGEFLAG(Active, active, PF_HEAD) - TESTCLEARFLAG(Active, active, PF_HEAD) -__PAGEFLAG(Slab, slab, PF_NO_TAIL) -__PAGEFLAG(SlobFree, slob_free, PF_NO_TAIL) -PAGEFLAG(Checked, checked, PF_NO_COMPOUND) /* Used by some filesystems */ - -/* Xen */ -PAGEFLAG(Pinned, pinned, PF_NO_COMPOUND) - TESTSCFLAG(Pinned, pinned, PF_NO_COMPOUND) -PAGEFLAG(SavePinned, savepinned, PF_NO_COMPOUND); -PAGEFLAG(Foreign, foreign, PF_NO_COMPOUND); - -PAGEFLAG(Reserved, reserved, PF_NO_COMPOUND) - __CLEARPAGEFLAG(Reserved, reserved, PF_NO_COMPOUND) -PAGEFLAG(SwapBacked, swapbacked, PF_NO_TAIL) - __CLEARPAGEFLAG(SwapBacked, swapbacked, PF_NO_TAIL) - __SETPAGEFLAG(SwapBacked, swapbacked, PF_NO_TAIL) - -/* - * Private page markings that may be used by the filesystem that owns the page - * for its own purposes. - * - PG_private and PG_private_2 cause releasepage() and co to be invoked - */ -PAGEFLAG(Private, private, PF_ANY) __SETPAGEFLAG(Private, private, PF_ANY) - __CLEARPAGEFLAG(Private, private, PF_ANY) -PAGEFLAG(Private2, private_2, PF_ANY) TESTSCFLAG(Private2, private_2, PF_ANY) -PAGEFLAG(OwnerPriv1, owner_priv_1, PF_ANY) - TESTCLEARFLAG(OwnerPriv1, owner_priv_1, PF_ANY) - -/* - * Only test-and-set exist for PG_writeback. The unconditional operators are - * risky: they bypass page accounting. - */ -TESTPAGEFLAG(Writeback, writeback, PF_NO_COMPOUND) - TESTSCFLAG(Writeback, writeback, PF_NO_COMPOUND) -PAGEFLAG(MappedToDisk, mappedtodisk, PF_NO_COMPOUND) - -/* PG_readahead is only used for reads; PG_reclaim is only for writes */ -PAGEFLAG(Reclaim, reclaim, PF_NO_COMPOUND) - TESTCLEARFLAG(Reclaim, reclaim, PF_NO_COMPOUND) -PAGEFLAG(Readahead, reclaim, PF_NO_COMPOUND) - TESTCLEARFLAG(Readahead, reclaim, PF_NO_COMPOUND) - -#ifdef CONFIG_HIGHMEM -/* - * Must use a macro here due to header dependency issues. page_zone() is not - * available at this point. - */ -#define PageHighMem(__p) is_highmem_idx(page_zonenum(__p)) -#else -PAGEFLAG_FALSE(HighMem) -#endif - -#ifdef CONFIG_SWAP -PAGEFLAG(SwapCache, swapcache, PF_NO_COMPOUND) -#else -PAGEFLAG_FALSE(SwapCache) -#endif - -PAGEFLAG(Unevictable, unevictable, PF_HEAD) - __CLEARPAGEFLAG(Unevictable, unevictable, PF_HEAD) - TESTCLEARFLAG(Unevictable, unevictable, PF_HEAD) - -#ifdef CONFIG_MMU -PAGEFLAG(Mlocked, mlocked, PF_NO_TAIL) - __CLEARPAGEFLAG(Mlocked, mlocked, PF_NO_TAIL) - TESTSCFLAG(Mlocked, mlocked, PF_NO_TAIL) -#else -PAGEFLAG_FALSE(Mlocked) __CLEARPAGEFLAG_NOOP(Mlocked) - TESTSCFLAG_FALSE(Mlocked) -#endif - -#ifdef CONFIG_ARCH_USES_PG_UNCACHED -PAGEFLAG(Uncached, uncached, PF_NO_COMPOUND) -#else -PAGEFLAG_FALSE(Uncached) -#endif - -#ifdef CONFIG_MEMORY_FAILURE -PAGEFLAG(HWPoison, hwpoison, PF_ANY) -TESTSCFLAG(HWPoison, hwpoison, PF_ANY) -#define __PG_HWPOISON (1UL << PG_hwpoison) -#else -PAGEFLAG_FALSE(HWPoison) -#define __PG_HWPOISON 0 -#endif - -#if defined(CONFIG_IDLE_PAGE_TRACKING) && defined(CONFIG_64BIT) -TESTPAGEFLAG(Young, young, PF_ANY) -SETPAGEFLAG(Young, young, PF_ANY) -TESTCLEARFLAG(Young, young, PF_ANY) -PAGEFLAG(Idle, idle, PF_ANY) -#endif +#include /* * On an anonymous page mapped into a user virtual memory area, @@ -390,8 +195,6 @@ static __always_inline int PageKsm(struct page *page) return ((unsigned long)page->mapping & PAGE_MAPPING_FLAGS) == (PAGE_MAPPING_ANON | PAGE_MAPPING_KSM); } -#else -TESTPAGEFLAG_FALSE(Ksm) #endif u64 stable_page_flags(struct page *page); @@ -434,8 +237,6 @@ static __always_inline void SetPageUptodate(struct page *page) set_bit(PG_uptodate, &page->flags); } -CLEARPAGEFLAG(Uptodate, uptodate, PF_NO_TAIL) - int test_clear_page_writeback(struct page *page); int __test_set_page_writeback(struct page *page, bool keep_write); @@ -454,8 +255,6 @@ static inline void set_page_writeback_keepwrite(struct page *page) test_set_page_writeback_keepwrite(page); } -__PAGEFLAG(Head, head, PF_ANY) CLEARPAGEFLAG(Head, head, PF_ANY) - static __always_inline void set_compound_head(struct page *page, struct page *head) { WRITE_ONCE(page->compound_head, (unsigned long)head + 1); @@ -481,8 +280,6 @@ int PageHuge(struct page *page); int PageHeadHuge(struct page *page); bool page_huge_active(struct page *page); #else -TESTPAGEFLAG_FALSE(Huge) -TESTPAGEFLAG_FALSE(HeadHuge) static inline bool page_huge_active(struct page *page) { @@ -555,14 +352,6 @@ static inline int TestClearPageDoubleMap(struct page *page) VM_BUG_ON_PAGE(!PageHead(page), page); return test_and_clear_bit(PG_double_map, &page[1].flags); } - -#else -TESTPAGEFLAG_FALSE(TransHuge) -TESTPAGEFLAG_FALSE(TransCompound) -TESTPAGEFLAG_FALSE(TransTail) -TESTPAGEFLAG_FALSE(DoubleMap) - TESTSETFLAG_FALSE(DoubleMap) - TESTCLEARFLAG_FALSE(DoubleMap) #endif /* @@ -684,10 +473,6 @@ static inline int page_has_private(struct page *page) return !!(page->flags & PAGE_FLAGS_PRIVATE); } -#undef PF_ANY -#undef PF_HEAD -#undef PF_NO_TAIL -#undef PF_NO_COMPOUND #endif /* !__GENERATING_BOUNDS_H */ #endif /* PAGE_FLAGS_H */ diff --git a/mm/page-flags.tbl b/mm/page-flags.tbl new file mode 100644 index 000000000000..1eb2f90f487a --- /dev/null +++ b/mm/page-flags.tbl @@ -0,0 +1,102 @@ +Locked locked no_tail test __set __clear +Error error no_compound test set clear testclear +Referenced referenced head test set clear testclear __set +Dirty dirty head test set clear testset testclear __clear +LRU lru head test set clear __clear +Active active head test set clear testclear __clear +Slab slab no_tail test __set __clear +SlobFree slob_free no_tail test __set __clear + +/* Used by some filesystems */ +Checked checked no_compound test set clear + +/* Xen */ +Pinned pinned no_compound test set clear testset testclear +SavePinned savepinned no_compound test set clear +Foreign foreign no_compound test set clear + +Reserved reserved no_compound test set clear __clear +SwapBacked swapbacked no_tail test set clear __set __clear + +/* + * Private page markings that may be used by the filesystem that owns the page + * for its own purposes. + * - PG_private and PG_private_2 cause releasepage() and co to be invoked + */ + +Private private any test set clear __set __clear +Private2 private_2 any test set clear testset testclear + +/* + * Only test-and-set exist for PG_writeback. The unconditional operators are + * risky: they bypass page accounting. + */ +Writeback writeback no_compound test testset testclear +MappedToDisk mappedtodisk no_compound test set clear + +/* PG_readahead is only used for reads; PG_reclaim is only for writes */ +Reclaim reclaim no_compound test set clear testclear +Readahead reclaim no_compound test set clear testclear + +#ifdef CONFIG_HIGHMEM +/* + * Must use a macro here due to header dependency issues. page_zone() is not + * available at this point. + */ +#define PageHighMem(__p) is_highmem_idx(page_zonenum(__p)) +#else +HighMem null any testfalse setnoop clearnoop +#endif + +#ifdef CONFIG_SWAP +SwapCache swapcache no_compound test set clear +#else +SwapCache null any testfalse setnoop clearnoop +#endif + +Unevictable unevictable head test set clear testclear __clear + +#ifdef CONFIG_MMU +Mlocked mlocked no_tail test set clear testset testclear __clear +#else +Mlocked null any testfalse setnoop clearnoop testsetfalse testclearfalse __clearnoop +#endif + +#ifdef CONFIG_ARCH_USES_PG_UNCACHED +Uncached uncached no_compound test set clear +#else +Uncached uncached any testfalse setnoop clearnoop +#endif + +#ifdef CONFIG_MEMORY_FAILURE +HWPoison hwpoison any test set clear testset testclear +#define __PG_HWPOISON (1UL << PG_hwpoison) +#else +HWPoison hwpoison any testfalse setnoop clearnoop +#define __PG_HWPOISON 0 +#endif + +#if defined(CONFIG_IDLE_PAGE_TRACKING) && defined(CONFIG_64BIT) +Young young any test set testclear +Idle idle any test set clear +#endif + +#ifndef CONFIG_KSM +Ksm null any testfalse +#endif + +Uptodate uptodate no_tail clear + +Head head any test clear __set __clear + +#ifndef CONFIG_HUGETLB_PAGE +Huge null any testfalse +HeadHuge null any testfalse +#endif + +#ifndef CONFIG_TRANSPARENT_HUGEPAGE +TransHuge null any testfalse +TransCompound null any testfalse +TransTail null any testfalse +DoubleMap null any testfalse testsetfalse testclearfalse +#endif diff --git a/scripts/mkpageflags.sh b/scripts/mkpageflags.sh new file mode 100755 index 000000000000..29d46bccaea4 --- /dev/null +++ b/scripts/mkpageflags.sh @@ -0,0 +1,151 @@ +#!/bin/sh -efu + +fatal() { + echo "$@" >&2 + exit 1 +} + +any() { + echo "(__p)" +} + +head() { + echo "compound_head(__p)" +} + +no_tail() { + local enforce="${1:+VM_BUG_ON_PGFLAGS(PageTail(__p), __p);}" + + echo "({$enforce compound_head(__p);})" +} + +no_compound() { + local enforce="${1:+VM_BUG_ON_PGFLAGS(PageCompound(__p), __p);}" + + echo "({$enforce __p;})" +} + +generate_test() { + local op="$1"; shift + local uname="$1"; shift + local lname="$1"; shift + local page="$1"; shift + + cat <flags); \\ + else \\ + BUILD_BUG(); \\ + ret; \\ +}) + +EOF +} + +generate_mod() { + local op="$1"; shift + local uname="$1"; shift + local lname="$1"; shift + local page="$1"; shift + + cat <flags); \\ + else \\ + BUILD_BUG(); \\ +} while (0) + +EOF +} + +generate_false() { + local uname="$1"; shift + + cat <