concurrent pagecache (against 2.6.19-rt)
Subsystems (17)
cover concurrent pagecache (against 2.6.19-rt)
Based on Nick's lockless (read-side) pagecache patches (included in the series) here an attempt to make the write side concurrent.
The patches survive some beating under PREEMPT_RT, but no performance measurements have been done yet.
I know everybody is busy stuffing patches Linus' way and should probably wait a bit so folks have time to look, but here goes anyway...
Comment away ;-)
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 1/16 radix-tree: RCU lockless readside
Make radix tree lookups safe to be performed without locks. Readers are protected against nodes being deleted by using RCU based freeing. Readers are protected against new node insertion by using memory barriers to ensure the node itself will be properly written before it is visible in the radix tree.
Each radix tree node keeps a record of their height (above leaf nodes). This height does not change after insertion -- when the radix tree is extended, higher nodes are only inserted in the top. So a lookup can take the pointer to what is *now* the root node, and traverse down it even if the tree is concurrently extended and this node becomes a subtree of a new root.
"Direct" pointers (tree height of 0, where root->rnode points directly to the data item) are handled by using the low bit of the pointer to signal whether rnode is a direct pointer or a pointer to a radix tree node.
When a reader wants to traverse the next branch, they will take a copy of the pointer. This pointer will be either NULL (and the branch is empty) or non-NULL (and will point to a valid node).
---
include/linux/radix-tree.h | 101 +++++++++++++ lib/radix-tree.c | 327 +++++++++++++++++++++++++++++++-------------- mm/migrate.c | 19 +- 3 files changed, 340 insertions(+), 107 deletions(-)
include/linux/radix-tree.h +101 -0
@@ -1,6 +1,7 @@/* * Copyright (C) 2001 Momchil Velikov * Portions Copyright (C) 2001 Christoph Hellwig +* Copyright (C) 2006 Nick Piggin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as
@@ -22,6 +23,35 @@#include <linux/sched.h> #include <linux/preempt.h> #include <linux/types.h> +#include <linux/kernel.h> +#include <linux/rcupdate.h> + +/* +* A direct pointer (root->rnode pointing directly to a data item, +* rather than another radix_tree_node) is signalled by the low bit +* set in the root->rnode pointer. +* +* In this case root->height is also NULL, but the direct pointer tests are +* needed for RCU lookups when root->height is unreliable. +*/ +#define RADIX_TREE_DIRECT_PTR 1 + +static inline void *radix_tree_ptr_to_direct (void *ptr) +{ +return (void *)((unsigned long )ptr |RADIX_TREE_DIRECT_PTR ); +} + +static inline void *radix_tree_direct_to_ptr (void *ptr) +{ +return (void *)((unsigned long )ptr & ~RADIX_TREE_DIRECT_PTR ); +} + +static inline int radix_tree_is_direct_ptr (void *ptr) +{ +return (int )((unsigned long )ptr &RADIX_TREE_DIRECT_PTR ); +} + +/*** radix-tree API starts here ***/ #define RADIX_TREE_MAX_TAGS 2
@@ -48,6 +78,77 @@ do { \ (root)->rnode =NULL ; \ }while (0 ) +/** +* Radix-tree synchronization +* +* The radix-tree API requires that users provide all synchronisation (with +* specific exceptions, noted below). +* +* Synchronization of access to the data items being stored in the tree, and +* management of their lifetimes must be completely managed by API users. +* +* For API usage, in general, +* - any function _modifying_ the the tree or tags (inserting or deleting +* items, setting or clearing tags must exclude other modifications, and +* exclude any functions reading the tree. +* - any function _reading_ the the tree or tags (looking up items or tags, +* gang lookups) must exclude modifications to the tree, but may occur +* concurrently with other readers. +* +* The notable exceptions to this rule are the following functions: +* radix_tree_lookup +* radix_tree_tag_get +* radix_tree_gang_lookup +* radix_tree_gang_lookup_tag +* radix_tree_tagged +* +* The first 4 functions are able to be called locklessly, using RCU. The +* caller must ensure calls to these functions are made within rcu_read_lock() +* regions. Other readers (lock-free or otherwise) and modifications may be +* running concurrently. +* +* It is still required that the caller manage the synchronization and lifetimes +* of the items. So if RCU lock-free lookups are used, typically this would mean +* that the items have their own locks, or are amenable to lock-free access; and +* that the items are freed by RCU (or only freed after having been deleted from +* the radix tree *and* a synchronize_rcu() grace period). +* +* (Note, rcu_assign_pointer and rcu_dereference are not needed to control +* access to data items when inserting into or looking up from the radix tree) +* +* radix_tree_tagged is able to be called without locking or RCU. +*/ + +/** +* radix_tree_deref_slot - dereference a slot +* @pslot: pointer to slot, returned by radix_tree_lookup_slot +* Returns: item that was stored in that slot with any direct pointer flag +* removed. +* +* For use with radix_tree_lookup_slot(). Caller must hold tree at least read +* locked across slot lookup and dereference. More likely, will be used with +* radix_tree_replace_slot(), as well, so caller will hold tree write locked. +*/ +static inline void *radix_tree_deref_slot(void **pslot) +{ +return radix_tree_direct_to_ptr (*pslot); +} +/** +* radix_tree_replace_slot - replace item in a slot +* @pslot: pointer to slot, returned by radix_tree_lookup_slot +* @item: new item to store in the slot. +* +* For use with radix_tree_lookup_slot(). Caller must hold tree write locked +* across slot lookup and replacement. +*/ +static inline void radix_tree_replace_slot (void **pslot,void *item) +{ +BUG_ON (radix_tree_is_direct_ptr (item)); +rcu_assign_pointer (*pslot, + (void *)((unsigned long )item | + ((unsigned long )*pslot &RADIX_TREE_DIRECT_PTR ))); +} +int radix_tree_insert (struct radix_tree_root *,unsigned long ,void *);void *radix_tree_lookup (struct radix_tree_root *,unsigned long );void **radix_tree_lookup_slot (struct radix_tree_root *,unsigned long );
lib/radix-tree.c +227 -100
611 lines not on this page · raw message
mm/migrate.c +12 -7
@@ -294,7 +294,7 @@ out:static int migrate_page_move_mapping (struct address_space *mapping,struct page *newpage,struct page *page) { -struct page **radix_pointer; +void **pslot;if (!mapping) {/* Anonymous page */
@@ -305,12 +305,11 @@ static int migrate_page_move_mapping(strwrite_lock_irq (&mapping->tree_lock); - radix_pointer = (struct page **)radix_tree_lookup_slot ( - &mapping->page_tree, -page_index (page)); + pslot =radix_tree_lookup_slot (&mapping->page_tree, +page_index (page));if (page_count (page) !=2 + !!PagePrivate (page) || - *radix_pointer != page) { + (struct page *)radix_tree_deref_slot (pslot) != page) {write_unlock_irq (&mapping->tree_lock);return -EAGAIN ; }
@@ -318,7 +317,7 @@ static int migrate_page_move_mapping(str/* * Now we know that no one else is looking at the page. */ -get_page (newpage); +get_page (newpage);/* add cache reference */ #ifdef CONFIG_SWAP if (PageSwapCache (page)) {SetPageSwapCache (newpage);
@@ -326,8 +325,14 @@ static int migrate_page_move_mapping(str }#endif - *radix_pointer = newpage; +radix_tree_replace_slot (pslot, newpage); + +/* +* Drop cache reference from old page. +* We know this isn't the last reference. +*/ __put_page (page); +write_unlock_irq (&mapping->tree_lock);return 0 ;
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 2/16 radix-tree: use indirect bit
In reply to Peter ZijlstraRather than sign direct radix-tree pointers with a special bit, sign the indirect one that hangs off the root. This means that, given a lookup_slot operation, the invalid result will be differentiated from the valid (previously, valid results could have the bit either set or clear).
This does not affect slot lookups which occur under lock -- they can never return an invalid result. Is needed in future for lockless pagecache.
--- include/linux/radix-tree.h | 40 ++++++++++++++------------ lib/radix-tree.c | 69 ++++++++++++++++++++++++++++----------------- 2 files changed, 65 insertions(+), 44 deletions(-)
include/linux/radix-tree.h +22 -18
@@ -27,28 +27,31 @@#include <linux/rcupdate.h> /* -* A direct pointer (root->rnode pointing directly to a data item, -* rather than another radix_tree_node) is signalled by the low bit -* set in the root->rnode pointer. -* -* In this case root->height is also NULL, but the direct pointer tests are -* needed for RCU lookups when root->height is unreliable. +* An indirect pointer (root->rnode pointing to a radix_tree_node, rather +* than a data item) is signalled by the low bit set in the root->rnode +* pointer. +* +* In this case root->height is > 0, but the indirect pointer tests are +* needed for RCU lookups (because root->height is unreliable). The only +* time callers need worry about this is when doing a lookup_slot under +* RCU. */ -#define RADIX_TREE_DIRECT_PTR 1 +#define RADIX_TREE_INDIRECT_PTR 1 +#define RADIX_TREE_RETRY ((void *)-1UL) -static inline void *radix_tree_ptr_to_direct (void *ptr) +static inline void *radix_tree_ptr_to_indirect (void *ptr) { -return (void *)((unsigned long )ptr |RADIX_TREE_DIRECT_PTR ); +return (void *)((unsigned long )ptr |RADIX_TREE_INDIRECT_PTR ); } -static inline void *radix_tree_direct_to_ptr (void *ptr) +static inline void *radix_tree_indirect_to_ptr (void *ptr) { -return (void *)((unsigned long )ptr & ~RADIX_TREE_DIRECT_PTR ); +return (void *)((unsigned long )ptr & ~RADIX_TREE_INDIRECT_PTR ); } -static inline int radix_tree_is_direct_ptr (void *ptr) +static inline int radix_tree_is_indirect_ptr (void *ptr) { -return (int )((unsigned long )ptr &RADIX_TREE_DIRECT_PTR ); +return (int )((unsigned long )ptr &RADIX_TREE_INDIRECT_PTR ); }/*** radix-tree API starts here ***/
@@ -131,7 +134,10 @@ do { \*/ static inline void *radix_tree_deref_slot (void **pslot) { -return radix_tree_direct_to_ptr (*pslot); +void *ret = *pslot; +if (unlikely (radix_tree_is_indirect_ptr (ret))) + ret =RADIX_TREE_RETRY ; +return ret; }/** * radix_tree_replace_slot - replace item in a slot
@@ -143,10 +149,8 @@ static inline void *radix_tree_deref_slo*/ static inline void radix_tree_replace_slot (void **pslot,void *item) { -BUG_ON (radix_tree_is_direct_ptr (item)); -rcu_assign_pointer (*pslot, - (void *)((unsigned long )item | - ((unsigned long )*pslot &RADIX_TREE_DIRECT_PTR ))); +BUG_ON (radix_tree_is_indirect_ptr (item)); +rcu_assign_pointer (*pslot, item); }int radix_tree_insert (struct radix_tree_root *,unsigned long ,void *);
lib/radix-tree.c +43 -26
200 lines not on this page · raw message
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 3/16 radix-tree: gang_lookup_slot
In reply to Peter ZijlstraIntroduce a gang_lookup_slot function which is used by lockless pagecache.
--- include/linux/radix-tree.h | 7 +++ lib/radix-tree.c | 86 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 82 insertions(+), 11 deletions(-)
include/linux/radix-tree.h +6 -1
@@ -100,12 +100,14 @@ do { \* * The notable exceptions to this rule are the following functions: * radix_tree_lookup +* radix_tree_lookup_slot * radix_tree_tag_get * radix_tree_gang_lookup +* radix_tree_gang_lookup_slot * radix_tree_gang_lookup_tag * radix_tree_tagged * -* The first 4 functions are able to be called locklessly, using RCU. The +* The first 6 functions are able to be called locklessly, using RCU. The * caller must ensure calls to these functions are made within rcu_read_lock() * regions. Other readers (lock-free or otherwise) and modifications may be * running concurrently.
@@ -160,6 +162,9 @@ void *radix_tree_delete(struct radix_treunsigned int radix_tree_gang_lookup (struct radix_tree_root *root,void **results,unsigned long first_index,unsigned int max_items); +unsigned int +radix_tree_gang_lookup_slot (struct radix_tree_root *root,void ***results, +unsigned long first_index,unsigned int max_items);/* * On a mutex based kernel we can freely schedule within the radix code: */
lib/radix-tree.c +76 -10
128 lines not on this page · raw message
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 4/16 radix-tree: gang_lookup_tag_slot
In reply to Peter ZijlstraSimple implementation of radix_tree_gang_lookup_tag_slot()
--- include/linux/radix-tree.h | 5 ++ lib/radix-tree.c | 81 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 4 deletions(-)
include/linux/radix-tree.h +5 -0
@@ -105,6 +105,7 @@ do { \* radix_tree_gang_lookup * radix_tree_gang_lookup_slot * radix_tree_gang_lookup_tag +* radix_tree_gang_lookup_tag_slot * radix_tree_tagged * * The first 6 functions are able to be called locklessly, using RCU. The
@@ -188,6 +189,10 @@ unsigned int radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items, unsigned int tag); +unsigned int +radix_tree_gang_lookup_tag_slot (struct radix_tree_root *root,void ***results, +unsigned long first_index,unsigned int max_items, +unsigned int tag);int radix_tree_tagged (struct radix_tree_root *root,unsigned int tag);static inline void radix_tree_preload_end (void )
lib/radix-tree.c +77 -4
105 lines not on this page · raw message
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 5/16 mm: speculative get page
In reply to Peter ZijlstraIf 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).
[Hugh notices that PG_nonewrefs might be dispensed with entirely
if current SetPageNoNewRefs instead atomically save the page count and temporarily set it to zero.
This is a nice idea, and simplifies find_get_page very much, but cannot be applied to all current SetPageNoNewRefs sites. Need to verify that add_to_page_cache and add_to_swap_cache can cope without it or make do some other way.
In the meantime, this version is a slightly more mechanical replacement.]
--- include/linux/page-flags.h | 8 +++ include/linux/pagemap.h | 105 +++++++++++++++++++++++++++++++++++++++++++++ mm/filemap.c | 4 + mm/migrate.c | 9 +++ mm/swap_state.c | 4 + mm/vmscan.c | 12 +++-- 6 files changed, 136 insertions(+), 6 deletions(-)
include/linux/page-flags.h +7 -1
@@ -91,7 +91,8 @@#define PG_nosave_free 18/* Used for system suspend/resume */ #define PG_buddy 19/* Page is free, on buddy lists */ - +#define PG_nonewrefs 20/* Block concurrent pagecache lookups +* while testing refcount */ #if (BITS_PER_LONG >32 )/* * 64-bit-only flags build down from bit 31
@@ -251,6 +252,11 @@ static inline void SetPageUptodate(struc#define SetPageUncached (page) set_bit(PG_uncached, &(page)->flags)#define ClearPageUncached (page) clear_bit(PG_uncached, &(page)->flags) +#define PageNoNewRefs (page) test_bit(PG_nonewrefs, &(page)->flags) +#define SetPageNoNewRefs (page) set_bit(PG_nonewrefs, &(page)->flags) +#define ClearPageNoNewRefs (page) clear_bit(PG_nonewrefs, &(page)->flags) +#define __ClearPageNoNewRefs (page) __clear_bit(PG_nonewrefs, &(page)->flags) +struct page ;/* forward declaration */ int test_clear_page_dirty (struct page *page);
include/linux/pagemap.h +105 -0
117 lines not on this page · raw message
mm/vmscan.c +9 -3
@@ -390,6 +390,8 @@ int remove_mapping(struct address_spaceBUG_ON (!PageLocked (page));BUG_ON (mapping !=page_mapping (page)); +SetPageNoNewRefs (page); +smp_wmb ();write_lock_irq (&mapping->tree_lock);/* * The non racy check for a busy page.
@@ -427,17 +429,21 @@ int remove_mapping(struct address_space__delete_from_swap_cache (page);write_unlock_irq (&mapping->tree_lock);swap_free (swap); -__put_page (page);/* The pagecache ref */ -return 1 ; + goto free_it; }__remove_from_page_cache (page);write_unlock_irq (&mapping->tree_lock); -__put_page (page); + +free_it: +smp_wmb (); +__ClearPageNoNewRefs (page); +__put_page (page);/* The pagecache ref */ return 1 ; cannot_free:write_unlock_irq (&mapping->tree_lock); +ClearPageNoNewRefs (page);return 0 ; }
mm/filemap.c +4 -0
@@ -440,6 +440,8 @@ int add_to_page_cache(struct page *page,int error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM);if (error ==0 ) { +SetPageNoNewRefs (page); +smp_wmb ();write_lock_irq (&mapping->tree_lock); error =radix_tree_insert (&mapping->page_tree, offset, page);if (!error) {
@@ -451,6 +453,8 @@ int add_to_page_cache(struct page *page,__inc_zone_page_state (page,NR_FILE_PAGES ); }write_unlock_irq (&mapping->tree_lock); +smp_wmb (); +ClearPageNoNewRefs (page);radix_tree_preload_end (); }return error;
mm/swap_state.c +4 -0
@@ -78,6 +78,8 @@ static int __add_to_swap_cache(struct paBUG_ON (PagePrivate (page)); error =radix_tree_preload (gfp_mask);if (!error) { +SetPageNoNewRefs (page); +smp_wmb ();write_lock_irq (&swapper_space.tree_lock); error =radix_tree_insert (&swapper_space.page_tree, entry.val, page);
@@ -90,6 +92,8 @@ static int __add_to_swap_cache(struct pa__inc_zone_page_state (page,NR_FILE_PAGES ); }write_unlock_irq (&swapper_space.tree_lock); +smp_wmb (); +ClearPageNoNewRefs (page);radix_tree_preload_end (); }return error;
mm/migrate.c +7 -2
@@ -303,6 +303,8 @@ static int migrate_page_move_mapping(strreturn 0 ; } +SetPageNoNewRefs (page); +smp_wmb ();write_lock_irq (&mapping->tree_lock); pslot = radix_tree_lookup_slot(&mapping->page_tree,
@@ -311,6 +313,7 @@ static int migrate_page_move_mapping(strif (page_count (page) !=2 + !!PagePrivate (page) || (struct page *)radix_tree_deref_slot (pslot) != page) {write_unlock_irq (&mapping->tree_lock); +ClearPageNoNewRefs (page);return -EAGAIN ; }
@@ -326,6 +329,10 @@ static int migrate_page_move_mapping(str#endif radix_tree_replace_slot (pslot, newpage); + page->mapping =NULL ; +write_unlock_irq (&mapping->tree_lock); +smp_wmb (); +ClearPageNoNewRefs (page);/* * Drop cache reference from old page.
@@ -333,8 +340,6 @@ static int migrate_page_move_mapping(str*/ __put_page (page); -write_unlock_irq (&mapping->tree_lock); -return 0 ; }
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 6/16 mm: lockless pagecache lookups
In reply to Peter ZijlstraCombine page_cache_get_speculative with lockless radix tree lookups to introduce lockless page cache lookups (ie. no mapping->tree_lock on the read-side).
The only atomicity changes this introduces is that the gang pagecache lookup functions now behave as if they are implemented with multiple find_get_page calls, rather than operating on a snapshot of the pages. In practice, this atomicity guarantee is not used anyway, and it is difficult to see how it could be. Gang pagecache lookups are designed to replace individual lookups, so these semantics are natural.
--- mm/filemap.c | 133 +++++++++++++++++++++++++++++++++++++--------------- mm/page-writeback.c | 8 +-- mm/readahead.c | 6 -- 3 files changed, 102 insertions(+), 45 deletions(-)
mm/filemap.c +97 -36
@@ -596,15 +596,31 @@ void fastcall __lock_page_nosync(struct* Is there a pagecache struct page at the given (mapping, offset) tuple? * If yes, increment its refcount and return it; if no, return NULL. */ -struct page *find_get_page (struct address_space *mapping,unsigned long offset) +struct page *find_get_page (struct address_space *mapping,unsigned long offset) { +void **pagep;struct page *page; -read_lock_irq (&mapping->tree_lock); - page =radix_tree_lookup (&mapping->page_tree, offset); -if (page) -page_cache_get (page); -read_unlock_irq (&mapping->tree_lock); +rcu_read_lock (); +repeat: + page =NULL ; + pagep =radix_tree_lookup_slot (&mapping->page_tree, offset); +if (pagep) { + page =radix_tree_deref_slot (pagep); +if (unlikely (!page || page ==RADIX_TREE_RETRY )) + goto repeat; + +if (!page_cache_get_speculative (page)) + goto repeat; + +/* Has the page moved? */ +if (unlikely (page != *pagep)) { +page_cache_release (page); + goto repeat; + } + } +rcu_read_unlock (); +return page; }EXPORT_SYMBOL (find_get_page);
@@ -644,26 +660,19 @@ struct page *find_lock_page(struct addre {struct page *page; -read_lock_irq (&mapping->tree_lock); repeat: - page =radix_tree_lookup (&mapping->page_tree, offset); + page =find_get_page (mapping, offset);if (page) { -page_cache_get (page); -if (TestSetPageLocked (page)) { -read_unlock_irq (&mapping->tree_lock); -__lock_page (page); -read_lock_irq (&mapping->tree_lock); - -/* Has the page been truncated while we slept? */ -if (unlikely (page->mapping != mapping || - page->index != offset)) { -unlock_page (page); -page_cache_release (page); - goto repeat; - } +lock_page (page); +/* Has the page been truncated? */ +if (unlikely (page->mapping != mapping + || page->index != offset)) { +unlock_page (page); +page_cache_release (page); + goto repeat; } } -read_unlock_irq (&mapping->tree_lock); +return page; }EXPORT_SYMBOL (find_lock_page);
@@ -733,13 +742,39 @@ unsigned find_get_pages(struct address_s {unsigned int i;unsigned int ret; +unsigned int nr_found; -read_lock_irq (&mapping->tree_lock); - ret =radix_tree_gang_lookup (&mapping->page_tree, - (void **)pages, start, nr_pages); -for (i =0 ; i < ret; i++) -page_cache_get (pages[i]); -read_unlock_irq (&mapping->tree_lock); +rcu_read_lock (); +restart: + nr_found =radix_tree_gang_lookup_slot (&mapping->page_tree, + (void ***)pages, start, nr_pages); + ret =0 ; +for (i =0 ; i < nr_found; i++) { +struct page *page; +repeat: + page =radix_tree_deref_slot ((void **)pages[i]); +if (unlikely (!page)) +continue ; +/* +* this can only trigger if nr_found == 1, making livelock +* a non issue. +*/ +if (unlikely (page ==RADIX_TREE_RETRY )) + goto restart; + +if (!page_cache_get_speculative (page)) + goto repeat; + +/* Has the page moved? */ +if (unlikely (page != *((void **)pages[i]))) { +page_cache_release (page); + goto repeat; + } + + pages[ret] = page; + ret++; + } +rcu_read_unlock ();return ret; }
@@ -760,19 +795,44 @@ unsigned find_get_pages_contig(struct ad {unsigned int i;unsigned int ret; +unsigned int nr_found; -read_lock_irq (&mapping->tree_lock); - ret =radix_tree_gang_lookup (&mapping->page_tree, - (void **)pages, index, nr_pages); -for (i =0 ; i < ret; i++) { -if (pages[i]->mapping ==NULL || pages[i]->index != index) +rcu_read_lock (); +restart: + nr_found =radix_tree_gang_lookup_slot (&mapping->page_tree, + (void ***)pages, index, nr_pages); + ret =0 ; +for (i =0 ; i < nr_found; i++) { +struct page *page; +repeat: + page =radix_tree_deref_slot ((void **)pages[i]); +if (unlikely (!page)) +continue ; +/* +* this can only trigger if nr_found == 1, making livelock +* a non issue. +*/ +if (unlikely (page ==RADIX_TREE_RETRY )) + goto restart; + +if (page->mapping ==NULL || page->index != index)break ; -page_cache_get (pages[i]); +if (!page_cache_get_speculative (page)) + goto repeat; + +/* Has the page moved? */ +if (unlikely (page != *((void **)pages[i]))) { +page_cache_release (page); + goto repeat; + } + + pages[ret] = page; + ret++; index++; } -read_unlock_irq (&mapping->tree_lock); -return i; +rcu_read_unlock (); +return ret; }/**
@@ -793,6 +853,7 @@ unsigned find_get_pages_tag(struct addreunsigned int ret;read_lock_irq (&mapping->tree_lock); +/* TODO: implement lookup_tag_slot and make this lockless */ ret =radix_tree_gang_lookup_tag (&mapping->page_tree, (void **)pages, *index, nr_pages, tag);for (i =0 ; i < ret; i++)
mm/readahead.c +2 -4
@@ -285,27 +285,25 @@ __do_page_cache_readahead(struct address/* * Preallocate as many pages as we will need. */ -read_lock_irq (&mapping->tree_lock);for (page_idx =0 ; page_idx < nr_to_read; page_idx++) {pgoff_t page_offset = offset + page_idx;if (page_offset > end_index)break ; +rcu_read_lock (); page =radix_tree_lookup (&mapping->page_tree, page_offset); +rcu_read_unlock ();if (page)continue ; -read_unlock_irq (&mapping->tree_lock); page =page_cache_alloc_cold (mapping); -read_lock_irq (&mapping->tree_lock);if (!page)break ; page->index = page_offset;list_add (&page->lru, &page_pool); ret++; } -read_unlock_irq (&mapping->tree_lock);/* * Now start the IO. We ignore I/O errors - if the page is not
mm/page-writeback.c +3 -5
@@ -956,17 +956,15 @@ int test_set_page_writeback(struct pageEXPORT_SYMBOL (test_set_page_writeback);/* -* Return true if any of the pages in the mapping are marged with the +* Return true if any of the pages in the mapping are marked with the * passed tag. */ int mapping_tagged (struct address_space *mapping,int tag) { -unsigned long flags;int ret; - -read_lock_irqsave (&mapping->tree_lock, flags); +rcu_read_lock (); ret =radix_tree_tagged (&mapping->page_tree, tag); -read_unlock_irqrestore (&mapping->tree_lock, flags); +rcu_read_unlock ();return ret; }EXPORT_SYMBOL (mapping_tagged);
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 7/16 mm: fix speculative page get preemption bug
In reply to Peter ZijlstraLivelock scenario pointed out by Nick.
SetPageNoNewRefs(page);
*** preempted here ***
page_cache_get_speculative() {
while (PageNoNewRefs(page)) /* livelock */
}--- include/linux/pagemap.h | 25 +++++++++++++++++++++++-- mm/filemap.c | 6 ++---- mm/migrate.c | 8 +++----- mm/swap_state.c | 6 ++---- mm/vmscan.c | 8 +++----- 5 files changed, 33 insertions(+), 20 deletions(-)
include/linux/pagemap.h +23 -2
@@ -53,6 +53,28 @@ static inline void mapping_set_gfp_mask(#define page_cache_release (page) put_page(page)void release_pages (struct page **pages,int nr,int cold); +static inline void set_page_no_new_refs (struct page *page) +{ +VM_BUG_ON (PageNoNewRefs (page)); +preempt_disable (); +SetPageNoNewRefs (page); +smp_wmb (); +} + +static inline void end_page_no_new_refs (struct page *page) +{ +VM_BUG_ON (!PageNoNewRefs (page)); +smp_wmb (); +ClearPageNoNewRefs (page); +preempt_enable (); +} + +static inline void wait_on_new_refs (struct page *page) +{ +while (unlikely (PageNoNewRefs (page))) +cpu_relax (); +} +/* * speculatively take a reference to a page. * If the page is free (_count == 0), then _count is untouched, and 0
@@ -128,8 +150,7 @@ static inline int page_cache_get_specula* page refcount has been raised. See below comment. */ -while (unlikely (PageNoNewRefs (page))) -cpu_relax (); +wait_on_new_refs (page);/* * smp_rmb is to ensure the load of page->flags (for PageNoNewRefs())
mm/filemap.c +2 -4
@@ -440,8 +440,7 @@ int add_to_page_cache(struct page *page,int error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM);if (error ==0 ) { -SetPageNoNewRefs (page); -smp_wmb (); +set_page_no_new_refs (page);write_lock_irq (&mapping->tree_lock); error =radix_tree_insert (&mapping->page_tree, offset, page);if (!error) {
@@ -453,8 +452,7 @@ int add_to_page_cache(struct page *page,__inc_zone_page_state (page,NR_FILE_PAGES ); }write_unlock_irq (&mapping->tree_lock); -smp_wmb (); -ClearPageNoNewRefs (page); +end_page_no_new_refs (page);radix_tree_preload_end (); }return error;
mm/migrate.c +3 -5
@@ -303,8 +303,7 @@ static int migrate_page_move_mapping(strreturn 0 ; } -SetPageNoNewRefs (page); -smp_wmb (); +set_page_no_new_refs (page);write_lock_irq (&mapping->tree_lock); pslot = radix_tree_lookup_slot(&mapping->page_tree,
@@ -313,7 +312,7 @@ static int migrate_page_move_mapping(strif (page_count (page) !=2 + !!PagePrivate (page) || (struct page *)radix_tree_deref_slot (pslot) != page) {write_unlock_irq (&mapping->tree_lock); -ClearPageNoNewRefs (page); +end_page_no_new_refs (page);return -EAGAIN ; }
@@ -331,8 +330,7 @@ static int migrate_page_move_mapping(strradix_tree_replace_slot (pslot, newpage); page->mapping =NULL ;write_unlock_irq (&mapping->tree_lock); -smp_wmb (); -ClearPageNoNewRefs (page); +end_page_no_new_refs (page);/* * Drop cache reference from old page.
mm/swap_state.c +2 -4
@@ -78,8 +78,7 @@ static int __add_to_swap_cache(struct paBUG_ON (PagePrivate (page)); error =radix_tree_preload (gfp_mask);if (!error) { -SetPageNoNewRefs (page); -smp_wmb (); +set_page_no_new_refs (page);write_lock_irq (&swapper_space.tree_lock); error =radix_tree_insert (&swapper_space.page_tree, entry.val, page);
@@ -92,8 +91,7 @@ static int __add_to_swap_cache(struct pa__inc_zone_page_state (page,NR_FILE_PAGES ); }write_unlock_irq (&swapper_space.tree_lock); -smp_wmb (); -ClearPageNoNewRefs (page); +end_page_no_new_refs (page);radix_tree_preload_end (); }return error;
mm/vmscan.c +3 -5
@@ -390,8 +390,7 @@ int remove_mapping(struct address_spaceBUG_ON (!PageLocked (page));BUG_ON (mapping !=page_mapping (page)); -SetPageNoNewRefs (page); -smp_wmb (); +set_page_no_new_refs (page);write_lock_irq (&mapping->tree_lock);/* * The non racy check for a busy page.
@@ -436,14 +435,13 @@ int remove_mapping(struct address_spacewrite_unlock_irq (&mapping->tree_lock); free_it: -smp_wmb (); -__ClearPageNoNewRefs (page); +end_page_no_new_refs (page);__put_page (page);/* The pagecache ref */ return 1 ; cannot_free:write_unlock_irq (&mapping->tree_lock); -ClearPageNoNewRefs (page); +end_page_no_new_refs (page);return 0 ; }
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 8/16 mm: speculative page get for PREEMPT_RT
In reply to Peter ZijlstraSince most of the locks are sleeping locks with PREEMPT_RT provide a sleeping implementation of wait_on_new_refs(). This also solves the preempt livelock and thus we can remove the preempt_disable()/ preempt_enable() from the PG_nonewrefs functions.
--- include/linux/pagemap.h | 51 +++++++++++++++++++++++++++++++++++++++++++++++- mm/filemap.c | 17 ++-------------- 2 files changed, 53 insertions(+), 15 deletions(-)
include/linux/pagemap.h +50 -1
@@ -13,6 +13,8 @@#include <linux/gfp.h> #include <linux/page-flags.h> #include <linux/hardirq.h> /* for in_interrupt() */ +#include <linux/wait.h> +#include <linux/hash.h> /* * Bits in mapping->flags. The lower __GFP_BITS_SHIFT bits are the page
@@ -53,6 +55,26 @@ static inline void mapping_set_gfp_mask(#define page_cache_release (page) put_page(page)void release_pages (struct page **pages,int nr,int cold); +/* +* In order to wait for pages to become available there must be +* waitqueues associated with pages. By using a hash table of +* waitqueues where the bucket discipline is to maintain all +* waiters on the same queue and wake all when any of the pages +* become available, and for the woken contexts to check to be +* sure the appropriate page became available, this saves space +* at a cost of "thundering herd" phenomena during rare hash +* collisions. +*/ +static inline wait_queue_head_t *page_waitqueue (struct page *page) +{ +const struct zone *zone =page_zone (page); + +return &zone->wait_table[hash_ptr (page, zone->wait_table_bits)]; +} + +extern int __sleep_on_page (void *); + +#ifndef CONFIG_PREEMPT_RT static inline void set_page_no_new_refs (struct page *page) {VM_BUG_ON (PageNoNewRefs (page));
@@ -74,6 +96,33 @@ static inline void wait_on_new_refs(struwhile (unlikely (PageNoNewRefs (page)))cpu_relax (); } +#else +static inline void set_page_no_new_refs (struct page *page) +{ +VM_BUG_ON (PageNoNewRefs (page)); +SetPageNoNewRefs (page); +smp_wmb (); +} + +static inline void end_page_no_new_refs (struct page *page) +{ +VM_BUG_ON (!PageNoNewRefs (page)); +smp_wmb (); +ClearPageNoNewRefs (page); +smp_mb__after_clear_bit (); +__wake_up_bit (page_waitqueue (page), &page->flags, PG_nonewrefs); +} + +static inline void wait_on_new_refs (struct page *page) +{ +might_sleep (); +if (unlikely (PageNoNewRefs (page))) { +DEFINE_WAIT_BIT (wait, &page->flags, PG_nonewrefs); +__wait_on_bit (page_waitqueue (page), &wait, __sleep_on_page, +TASK_UNINTERRUPTIBLE ); + } +} +#endif /* * speculatively take a reference to a page.
@@ -124,7 +173,7 @@ static inline int page_cache_get_specula {VM_BUG_ON (in_interrupt ()); -#ifndef CONFIG_SMP +#if !defined(CONFIG_SMP ) && !defined(CONFIG_PREEMPT_RT )# ifdef CONFIG_PREEMPT VM_BUG_ON (!in_atomic ());# endif
mm/filemap.c +3 -14
@@ -486,21 +486,10 @@ static int __sleep_on_page_lock(void *woreturn 0 ; } -/* -* In order to wait for pages to become available there must be -* waitqueues associated with pages. By using a hash table of -* waitqueues where the bucket discipline is to maintain all -* waiters on the same queue and wake all when any of the pages -* become available, and for the woken contexts to check to be -* sure the appropriate page became available, this saves space -* at a cost of "thundering herd" phenomena during rare hash -* collisions. -*/ -static wait_queue_head_t *page_waitqueue (struct page *page) +int __sleep_on_page (void *word) { -const struct zone *zone =page_zone (page); - -return &zone->wait_table[hash_ptr (page, zone->wait_table_bits)]; +schedule (); +return 0 ; }static inline void wake_up_page (struct page *page,int bit)
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 9/16 mm: speculative find_get_pages_tag
In reply to Peter Zijlstraimplement the speculative find_get_pages_tag.
--- mm/filemap.c | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-)
mm/filemap.c +33 -9
@@ -838,16 +838,40 @@ unsigned find_get_pages_tag(struct addre {unsigned int i;unsigned int ret; +unsigned int nr_found; -read_lock_irq (&mapping->tree_lock); -/* TODO: implement lookup_tag_slot and make this lockless */ - ret =radix_tree_gang_lookup_tag (&mapping->page_tree, - (void **)pages, *index, nr_pages, tag); -for (i =0 ; i < ret; i++) -page_cache_get (pages[i]); -if (ret) - *index = pages[ret -1 ]->index +1 ; -read_unlock_irq (&mapping->tree_lock); +rcu_read_lock (); +restart: + nr_found =radix_tree_gang_lookup_tag_slot (&mapping->page_tree, + (void ***)pages, *index, nr_pages, tag); + + ret =0 ; +for (i =0 ; i < nr_found; i++) { +struct page *page; +repeat: + page =radix_tree_deref_slot ((void **)pages[i]); +if (unlikely (!page)) +continue ; +/* +* this can only trigger if nr_found == 1, making livelocks +* a non issue. +*/ +if (unlikely (page ==RADIX_TREE_RETRY )) + goto restart; + +if (!page_cache_get_speculative (page)) + goto repeat; + +/* Has the page moved? */ +if (unlikely (page != *((void **)pages[i]))) { +page_cache_release (page); + goto repeat; + } + + pages[ret] = page; + ret++; + } +rcu_read_unlock ();return ret; }
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 10/16 mm: remove find_tylock_page
In reply to Peter Zijlstraits the last read_lock user of tree_lock, and since its unused remove it rather than convert it.
--- include/linux/pagemap.h | 2 -- mm/filemap.c | 20 -------------------- 2 files changed, 22 deletions(-)
mm/filemap.c +0 -20
@@ -613,26 +613,6 @@ repeat:EXPORT_SYMBOL (find_get_page);/** -* find_trylock_page - find and lock a page -* @mapping: the address_space to search -* @offset: the page index -* -* Same as find_get_page(), but trylock it instead of incrementing the count. -*/ -struct page *find_trylock_page (struct address_space *mapping,unsigned long offset) -{ -struct page *page; - -read_lock_irq (&mapping->tree_lock); - page =radix_tree_lookup (&mapping->page_tree, offset); -if (page &&TestSetPageLocked (page)) - page =NULL ; -read_unlock_irq (&mapping->tree_lock); -return page; -} -EXPORT_SYMBOL (find_trylock_page); - -/** * find_lock_page - locate, pin and lock a pagecache page * @mapping: the address_space to search * @offset: the page index
include/linux/pagemap.h +0 -2
@@ -251,8 +251,6 @@ extern struct page * find_get_page(struc unsigned long index);extern struct page *find_lock_page (struct address_space *mapping,unsigned long index); -extern __deprecated_for_modules struct page * find_trylock_page( -struct address_space *mapping,unsigned long index);extern struct page *find_or_create_page (struct address_space *mapping,unsigned long index,gfp_t gfp_mask); unsigned find_get_pages(struct address_space *mapping, pgoff_t start,
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 11/16 mm: change tree_lock into a spinlock
In reply to Peter Zijlstrawith all the read_lock uses of the tree_lock gone, change it into a spinlock.
--- fs/buffer.c | 4 ++-- fs/inode.c | 2 +- include/asm-arm/cacheflush.h | 4 ++-- include/asm-parisc/cacheflush.h | 4 ++-- include/linux/fs.h | 2 +- mm/filemap.c | 8 ++++---- mm/migrate.c | 6 +++--- mm/page-writeback.c | 18 +++++++++--------- mm/swap_state.c | 10 +++++----- mm/swapfile.c | 4 ++-- mm/truncate.c | 6 +++--- mm/vmscan.c | 8 ++++---- 12 files changed, 38 insertions(+), 38 deletions(-)
fs/buffer.c +2 -2
@@ -719,7 +719,7 @@ int __set_page_dirty_buffers(struct pagespin_unlock (&mapping->private_lock);if (!TestSetPageDirty (page)) { -write_lock_irq (&mapping->tree_lock); +spin_lock_irq (&mapping->tree_lock);if (page->mapping) {/* Race with truncate? */ if (mapping_cap_account_dirty (mapping))__inc_zone_page_state (page,NR_FILE_DIRTY );
@@ -727,7 +727,7 @@ int __set_page_dirty_buffers(struct pagepage_index (page), PAGECACHE_TAG_DIRTY); } -write_unlock_irq (&mapping->tree_lock); +spin_unlock_irq (&mapping->tree_lock);__mark_inode_dirty (mapping->host,I_DIRTY_PAGES );return 1 ; }
include/asm-arm/cacheflush.h +2 -2
@@ -354,9 +354,9 @@ extern void flush_ptrace_access(struct vextern void flush_dcache_page (struct page *);#define flush_dcache_mmap_lock (mapping) \ - write_lock_irq(&(mapping)->tree_lock) + spin_lock_irq(&(mapping)->tree_lock)#define flush_dcache_mmap_unlock (mapping) \ - write_unlock_irq(&(mapping)->tree_lock) + spin_unlock_irq(&(mapping)->tree_lock)#define flush_icache_user_range (vma,page,addr,len) \ flush_dcache_page(page)
include/asm-parisc/cacheflush.h +2 -2
@@ -57,9 +57,9 @@ flush_user_icache_range(unsigned long stextern void flush_dcache_page (struct page *page);#define flush_dcache_mmap_lock (mapping) \ - write_lock_irq(&(mapping)->tree_lock) + spin_lock_irq(&(mapping)->tree_lock)#define flush_dcache_mmap_unlock (mapping) \ - write_unlock_irq(&(mapping)->tree_lock) + spin_unlock_irq(&(mapping)->tree_lock)#define flush_icache_page (vma,page) do { flush_kernel_dcache_page(page); flush_kernel_icache_page(page_address(page)); } while (0)
include/linux/fs.h +1 -1
@@ -430,7 +430,7 @@ struct backing_dev_info;struct address_space {struct inode *host;/* owner: inode, block_device */ struct radix_tree_root page_tree;/* radix tree of all pages */ -rwlock_t tree_lock;/* and rwlock protecting it */ +spinlock_t tree_lock;/* and rwlock protecting it */ unsigned int i_mmap_writable;/* count VM_SHARED mappings */ struct prio_tree_root i_mmap;/* tree of private and shared mappings */ struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
mm/filemap.c +4 -4
@@ -128,9 +128,9 @@ void remove_from_page_cache(struct pageBUG_ON (!PageLocked (page)); -write_lock_irq (&mapping->tree_lock); +spin_lock_irq (&mapping->tree_lock);__remove_from_page_cache (page); -write_unlock_irq (&mapping->tree_lock); +spin_unlock_irq (&mapping->tree_lock); }static int sync_page (void *word)
@@ -441,7 +441,7 @@ int add_to_page_cache(struct page *page,if (error ==0 ) {set_page_no_new_refs (page); -write_lock_irq (&mapping->tree_lock); +spin_lock_irq (&mapping->tree_lock); error =radix_tree_insert (&mapping->page_tree, offset, page);if (!error) {page_cache_get (page);
@@ -451,7 +451,7 @@ int add_to_page_cache(struct page *page, mapping->nrpages++;__inc_zone_page_state (page,NR_FILE_PAGES ); } -write_unlock_irq (&mapping->tree_lock); +spin_unlock_irq (&mapping->tree_lock);end_page_no_new_refs (page);radix_tree_preload_end (); }
mm/migrate.c +3 -3
@@ -304,14 +304,14 @@ static int migrate_page_move_mapping(str }set_page_no_new_refs (page); -write_lock_irq (&mapping->tree_lock); +spin_lock_irq (&mapping->tree_lock); pslot =radix_tree_lookup_slot (&mapping->page_tree,page_index (page));if (page_count (page) !=2 + !!PagePrivate (page) || (struct page *)radix_tree_deref_slot (pslot) != page) { -write_unlock_irq (&mapping->tree_lock); +spin_unlock_irq (&mapping->tree_lock);end_page_no_new_refs (page);return -EAGAIN ; }
@@ -329,7 +329,7 @@ static int migrate_page_move_mapping(strradix_tree_replace_slot (pslot, newpage); page->mapping =NULL ; -write_unlock_irq (&mapping->tree_lock); +spin_unlock_irq (&mapping->tree_lock);end_page_no_new_refs (page);/*
mm/page-writeback.c +9 -9
@@ -762,7 +762,7 @@ int __set_page_dirty_nobuffers(struct pastruct address_space *mapping2;if (mapping) { -write_lock_irq (&mapping->tree_lock); +spin_lock_irq (&mapping->tree_lock); mapping2 =page_mapping (page);if (mapping2) {/* Race with truncate? */ BUG_ON (mapping2 != mapping);
@@ -772,7 +772,7 @@ int __set_page_dirty_nobuffers(struct paradix_tree_tag_set (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); } -write_unlock_irq (&mapping->tree_lock); +spin_unlock_irq (&mapping->tree_lock);if (mapping->host) {/* !PageAnon && !swapper_space */ __mark_inode_dirty(mapping->host,
@@ -852,12 +852,12 @@ int test_clear_page_dirty(struct page *punsigned long flags;if (mapping) { -write_lock_irqsave (&mapping->tree_lock, flags); +spin_lock_irqsave (&mapping->tree_lock, flags);if (TestClearPageDirty (page)) {radix_tree_tag_clear (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); -write_unlock_irqrestore (&mapping->tree_lock, flags); +spin_unlock_irqrestore (&mapping->tree_lock, flags);/* * We can continue to use `mapping' here because the * page is locked, which pins the address_space
@@ -868,7 +868,7 @@ int test_clear_page_dirty(struct page *p }return 1 ; } -write_unlock_irqrestore (&mapping->tree_lock, flags); +spin_unlock_irqrestore (&mapping->tree_lock, flags);return 0 ; }return TestClearPageDirty (page);
@@ -915,13 +915,13 @@ int test_clear_page_writeback(struct pagif (mapping) {unsigned long flags; -write_lock_irqsave (&mapping->tree_lock, flags); +spin_lock_irqsave (&mapping->tree_lock, flags); ret =TestClearPageWriteback (page);if (ret)radix_tree_tag_clear (&mapping->page_tree,page_index (page),PAGECACHE_TAG_WRITEBACK ); -write_unlock_irqrestore (&mapping->tree_lock, flags); +spin_unlock_irqrestore (&mapping->tree_lock, flags); }else { ret =TestClearPageWriteback (page); }
@@ -936,7 +936,7 @@ int test_set_page_writeback(struct pageif (mapping) {unsigned long flags; -write_lock_irqsave (&mapping->tree_lock, flags); +spin_lock_irqsave (&mapping->tree_lock, flags); ret =TestSetPageWriteback (page);if (!ret) radix_tree_tag_set(&mapping->page_tree,
@@ -946,7 +946,7 @@ int test_set_page_writeback(struct pageradix_tree_tag_clear (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); -write_unlock_irqrestore (&mapping->tree_lock, flags); +spin_unlock_irqrestore (&mapping->tree_lock, flags); }else { ret =TestSetPageWriteback (page); }
mm/swap_state.c +5 -5
@@ -38,7 +38,7 @@ static struct backing_dev_info swap_backstruct address_space swapper_space = { .page_tree =RADIX_TREE_INIT (GFP_ATOMIC |__GFP_NOWARN), - .tree_lock =__RW_LOCK_UNLOCKED (swapper_space.tree_lock), + .tree_lock =__SPIN_LOCK_UNLOCKED (swapper_space.tree_lock), .a_ops = &swap_aops, .i_mmap_nonlinear =LIST_HEAD_INIT (swapper_space.i_mmap_nonlinear), .backing_dev_info = &swap_backing_dev_info,
@@ -79,7 +79,7 @@ static int __add_to_swap_cache(struct pa error =radix_tree_preload (gfp_mask);if (!error) {set_page_no_new_refs (page); -write_lock_irq (&swapper_space.tree_lock); +spin_lock_irq (&swapper_space.tree_lock); error =radix_tree_insert (&swapper_space.page_tree, entry.val, page);if (!error) {
@@ -90,7 +90,7 @@ static int __add_to_swap_cache(struct pa total_swapcache_pages++;__inc_zone_page_state (page,NR_FILE_PAGES ); } -write_unlock_irq (&swapper_space.tree_lock); +spin_unlock_irq (&swapper_space.tree_lock);end_page_no_new_refs (page);radix_tree_preload_end (); }
@@ -202,9 +202,9 @@ void delete_from_swap_cache(struct page entry.val =page_private (page); -write_lock_irq (&swapper_space.tree_lock); +spin_lock_irq (&swapper_space.tree_lock);__delete_from_swap_cache (page); -write_unlock_irq (&swapper_space.tree_lock); +spin_unlock_irq (&swapper_space.tree_lock);swap_free (entry);page_cache_release (page);
mm/swapfile.c +2 -2
@@ -367,13 +367,13 @@ int remove_exclusive_swap_page(struct pa retval =0 ;if (p->swap_map[swp_offset (entry)] ==1 ) {/* Recheck the page count with the swapcache lock held.. */ -write_lock_irq (&swapper_space.tree_lock); +spin_lock_irq (&swapper_space.tree_lock);if ((page_count (page) ==2 ) && !PageWriteback (page)) {__delete_from_swap_cache (page);SetPageDirty (page); retval =1 ; } -write_unlock_irq (&swapper_space.tree_lock); +spin_unlock_irq (&swapper_space.tree_lock); }spin_unlock (&swap_lock);
mm/truncate.c +3 -3
@@ -304,18 +304,18 @@ invalidate_complete_page2(struct addressif (PagePrivate (page) && !try_to_release_page (page,GFP_KERNEL ))return 0 ; -write_lock_irq (&mapping->tree_lock); +spin_lock_irq (&mapping->tree_lock);if (PageDirty (page)) goto failed;BUG_ON (PagePrivate (page));__remove_from_page_cache (page); -write_unlock_irq (&mapping->tree_lock); +spin_unlock_irq (&mapping->tree_lock);ClearPageUptodate (page);page_cache_release (page);/* pagecache ref */ return 1 ; failed: -write_unlock_irq (&mapping->tree_lock); +spin_unlock_irq (&mapping->tree_lock);return 0 ; }
mm/vmscan.c +4 -4
@@ -391,7 +391,7 @@ int remove_mapping(struct address_spaceBUG_ON (mapping !=page_mapping (page));set_page_no_new_refs (page); -write_lock_irq (&mapping->tree_lock); +spin_lock_irq (&mapping->tree_lock);/* * The non racy check for a busy page. *
@@ -426,13 +426,13 @@ int remove_mapping(struct address_spaceif (PageSwapCache (page)) {swp_entry_t swap = { .val =page_private (page) };__delete_from_swap_cache (page); -write_unlock_irq (&mapping->tree_lock); +spin_unlock_irq (&mapping->tree_lock);swap_free (swap); goto free_it; }__remove_from_page_cache (page); -write_unlock_irq (&mapping->tree_lock); +spin_unlock_irq (&mapping->tree_lock); free_it:end_page_no_new_refs (page);
@@ -440,7 +440,7 @@ free_it:return 1 ; cannot_free: -write_unlock_irq (&mapping->tree_lock); +spin_unlock_irq (&mapping->tree_lock);end_page_no_new_refs (page);return 0 ; }
fs/inode.c +1 -1
@@ -193,7 +193,7 @@ void inode_init_once(struct inode *inodemutex_init (&inode->i_mutex);init_rwsem (&inode->i_alloc_sem);INIT_RADIX_TREE (&inode->i_data.page_tree,GFP_ATOMIC ); -rwlock_init (&inode->i_data.tree_lock); +spin_lock_init (&inode->i_data.tree_lock);spin_lock_init (&inode->i_data.i_mmap_lock);INIT_LIST_HEAD (&inode->i_data.private_list);spin_lock_init (&inode->i_data.private_lock);
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 12/16 radix-tree: concurrent write side support
In reply to Peter ZijlstraProvide support for concurrent write side operations without changing the API for all current uses.
Concurrency is realized by means of two locking models; the simple one is ladder locking, the more complex one is path locking.
Ladder locking is like walking down a ladder, you place your foot on a spoke below the one your other foot finds support etc.. There is no walking with both feet in the air. Likewise with walking a tree, you lock a node below the current node before releasing it.
This allows other modifying operations to start as soon as you release the lock on the root node and even complete before you if they walk another path downward.
The modifying operations: insert, lookup_slot and set_tag, use this simple method.
The more complex path locking method is needed for operations that need to walk upwards again after they walked down, those are: tag_clear and delete.
These lock their whole path downwards and release whole sections at points where it can be determined the walk upwards will stop, thus also allowing concurrency.
Finding the conditions for the terminated walk upwards while doing the downward walk is the 'interesting' part of this approach.
The remaining - unmodified - operations will have exclusive locking (since they're unmodified, they never move the lock downwards from the root node).
The API for this looks like:
DECLARE_RADIX_TREE_CONTEXT(ctx, &mapping->page_tree)
radix_tree_lock(&ctx)
... do _1_ modifying operation ...
radix_tree_unlock(&ctx)
Note that before the radix operation the root node is held and will provide exclusive locking, after the operation the held lock might only be enough to protect a single item.
--- include/linux/radix-tree.h | 87 +++++++++++++- init/Kconfig | 4 lib/radix-tree.c | 279 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 306 insertions(+), 64 deletions(-)
include/linux/radix-tree.h +81 -6
@@ -63,23 +63,73 @@ struct radix_tree_root {unsigned int height;gfp_t gfp_mask;struct radix_tree_node *rnode; +spinlock_t lock; +#ifdef CONFIG_RADIX_TREE_CONCURRENT +struct radix_tree_context *context; +struct task_struct *owner; +#endif }; +#ifdef CONFIG_RADIX_TREE_CONCURRENT +#define __RADIX_TREE_CONCURRENT_INIT \ + .context = NULL, \ + .owner = NULL, +#else +#define __RADIX_TREE_CONCURRENT_INIT +#endif +#define RADIX_TREE_INIT (mask) { \ .height = 0, \ .gfp_mask = (mask), \ .rnode = NULL, \ + .lock = __SPIN_LOCK_UNLOCKED(radix_tree_root.lock), \ + __RADIX_TREE_CONCURRENT_INIT \ }#define RADIX_TREE (name, mask) \ struct radix_tree_root name = RADIX_TREE_INIT(mask) -#define INIT_RADIX_TREE (root, mask) \ -do { \ - (root)->height = 0; \ - (root)->gfp_mask = (mask); \ - (root)->rnode = NULL; \ -} while (0) +static inline void INIT_RADIX_TREE (struct radix_tree_root *root,gfp_t gfp_mask) +{ + root->height =0 ; + root->gfp_mask = gfp_mask; + root->rnode =NULL ; +spin_lock_init (&root->lock); +#ifdef CONFIG_RADIX_TREE_CONCURRENT + root->context =NULL ; + root->owner =NULL ; +#endif +} + +struct radix_tree_context { +struct radix_tree_root *root; +#ifdef CONFIG_RADIX_TREE_CONCURRENT +spinlock_t *locked; +#endif +}; + +#ifdef CONFIG_RADIX_TREE_CONCURRENT +#define __RADIX_TREE_CONTEXT_INIT \ + .locked = NULL, +#else +#define __RADIX_TREE_CONTEXT_INIT +#endif + +#define DECLARE_RADIX_TREE_CONTEXT (context, tree) \ + struct radix_tree_context context = { \ + .root = (tree), \ + __RADIX_TREE_CONTEXT_INIT \ + } + +static inline void +init_radix_tree_context (struct radix_tree_context *wctx, +struct radix_tree_root *root) +{ + wctx->root = root; +#ifdef CONFIG_RADIX_TREE_CONCURRENT + wctx->locked =NULL ; +#endif +}/** * Radix-tree synchronization
@@ -156,6 +206,31 @@ static inline void radix_tree_replace_slrcu_assign_pointer (*pslot, item); } +static inline void radix_tree_lock (struct radix_tree_context *context) +{ +struct radix_tree_root *root = context->root; +rcu_read_lock (); +spin_lock (&root->lock); +#ifdef CONFIG_RADIX_TREE_CONCURRENT +BUG_ON (context->locked); + context->locked = &root->lock; + root->owner = current; + root->context = context; +#endif +} + +static inline void radix_tree_unlock (struct radix_tree_context *context) +{ +#ifdef CONFIG_RADIX_TREE_CONCURRENT +BUG_ON (!context->locked); +spin_unlock (context->locked); + context->locked =NULL ; +#else +spin_unlock (&context->root->lock); +#endif +rcu_read_unlock (); +} +int radix_tree_insert (struct radix_tree_root *,unsigned long ,void *);void *radix_tree_lookup (struct radix_tree_root *,unsigned long );void **radix_tree_lookup_slot (struct radix_tree_root *,unsigned long );
lib/radix-tree.c +221 -58
495 lines not on this page · raw message
init/Kconfig +4 -0
@@ -287,6 +287,10 @@ config TASK_XACCT config SYSCTL bool +config RADIX_TREE_CONCURRENT + bool "Enable concurrent radix tree operations (EXPERIMENTAL)" + default y if SMP + menuconfig EMBEDDED bool "Configure standard kernel features (for small systems)" help
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 13/16 atomic_ulong_t
In reply to Peter Zijlstraprovide an unsigned long atomic type.
--- include/asm-generic/atomic.h | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
include/asm-generic/atomic.h +45 -0
@@ -114,4 +114,49 @@ static inline void atomic_long_sub(long }#endif + +typedef atomic_long_t atomic_ulong_t ; + +#define ATOMIC_ULONG_INIT (i) ATOMIC_LONG_INIT(i) +static inline unsigned long atomic_ulong_read (atomic_ulong_t *l) +{ +atomic_long_t *v = (atomic_long_t *)l; + +return (unsigned long )atomic_long_read (v); +} + +static inline void atomic_ulong_set (atomic_ulong_t *l,unsigned long i) +{ +atomic_long_t *v = (atomic_long_t *)l; + +atomic_long_set (v, i); +} + +static inline void atomic_ulong_inc (atomic_ulong_t *l) +{ +atomic_long_t *v = (atomic_long_t *)l; + +atomic_long_inc (v); +} + +static inline void atomic_ulong_dec (atomic_ulong_t *l) +{ +atomic_long_t *v = (atomic_long_t *)l; + +atomic_long_dec (v); +} + +static inline void atomic_ulong_add (unsigned long i,atomic_ulong_t *l) +{ +atomic_long_t *v = (atomic_long_t *)l; + +atomic_long_add (i, v); +} + +static inline void atomic_ulong_sub (unsigned long i,atomic_ulong_t *l) +{ +atomic_long_t *v = (atomic_long_t *)l; + +atomic_long_sub (i, v); +}#endif
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 14/16 mm/fs: abstract address_space::nrpages
In reply to Peter ZijlstraCurrently the tree_lock protects mapping->nrpages, this will not be possible much longer. Hence abstract the access to this variable so that it can be easily replaced by an atomic_ulong_t.
--- arch/sh64/lib/dbg.c | 2 +- fs/block_dev.c | 2 +- fs/buffer.c | 2 +- fs/gfs2/glock.c | 2 +- fs/gfs2/glops.c | 6 +++--- fs/gfs2/meta_io.c | 2 +- fs/hugetlbfs/inode.c | 2 +- fs/inode.c | 10 +++++----- fs/jffs/inode-v23.c | 2 +- fs/jffs2/dir.c | 4 ++-- fs/jffs2/fs.c | 2 +- fs/libfs.c | 2 +- fs/nfs/inode.c | 6 +++--- fs/xfs/linux-2.6/xfs_vnode.h | 2 +- include/linux/fs.h | 22 +++++++++++++++++++++- include/linux/swap.h | 2 +- ipc/shm.c | 4 ++-- mm/filemap.c | 12 ++++++------ mm/shmem.c | 8 ++++---- mm/swap_state.c | 4 ++-- mm/truncate.c | 2 +- 21 files changed, 60 insertions(+), 40 deletions(-)
arch/sh64/lib/dbg.c +1 -1
@@ -424,6 +424,6 @@ void print_page(struct page *page)printk (" page[%p] -> index 0x%lx, count 0x%x, flags 0x%lx\n" , page, page->index,page_count (page), page->flags);printk (" address_space = %p, pages =%ld\n" , page->mapping, - page->mapping->nrpages); +mapping_nrpages (page->mapping)); }
fs/block_dev.c +1 -1
@@ -398,7 +398,7 @@ long nr_blockdev_pages(void)list_for_each (p, &all_bdevs) {struct block_device *bdev; bdev = list_entry(p, struct block_device, bd_list); - ret += bdev->bd_inode->i_mapping->nrpages; + ret +=mapping_nrpages (bdev->bd_inode->i_mapping); }spin_unlock (&bdev_lock);return ret;
fs/buffer.c +1 -1
@@ -335,7 +335,7 @@ void invalidate_bdev(struct block_device {struct address_space *mapping = bdev->bd_inode->i_mapping; -if (mapping->nrpages ==0 ) +if (mapping_nrpages (mapping) ==0 )return ;invalidate_bh_lrus ();
fs/gfs2/glock.c +1 -1
@@ -2128,7 +2128,7 @@ static int dump_glock(struct gfs2_glock (list_empty(&gl->gl_reclaim)) ? "no" : "yes");if (gl->gl_aspace)printk (KERN_INFO " aspace = 0x%p nrpages = %lu\n" , gl->gl_aspace, - gl->gl_aspace->i_mapping->nrpages); +mapping_nrpages (gl->gl_aspace->i_mapping));else printk (KERN_INFO " aspace = no\n" );printk (KERN_INFO " ail = %d\n" ,atomic_read (&gl->gl_ail_count));
fs/gfs2/glops.c +3 -3
@@ -123,7 +123,7 @@ static void gfs2_page_inval(struct gfs2_return ;truncate_inode_pages (inode->i_mapping,0 ); -gfs2_assert_withdraw (GFS2_SB (&ip->i_inode), !inode->i_mapping->nrpages); +gfs2_assert_withdraw (GFS2_SB (&ip->i_inode), !mapping_nrpages (inode->i_mapping));clear_bit (GIF_PAGED , &ip->i_flags); }
@@ -324,7 +324,7 @@ static int inode_go_demote_ok(struct gfsstruct gfs2_sbd *sdp = gl->gl_sbd;int demote =0 ; -if (!gl->gl_object && !gl->gl_aspace->i_mapping->nrpages) +if (!gl->gl_object && !mapping_nrpages (gl->gl_aspace->i_mapping)) demote =1 ;else if (!sdp->sd_args.ar_localcaching && time_after_eq(jiffies, gl->gl_stamp +
@@ -428,7 +428,7 @@ static void inode_greedy(struct gfs2_glostatic int rgrp_go_demote_ok (struct gfs2_glock *gl) { -return !gl->gl_aspace->i_mapping->nrpages; +return !mapping_nrpages (gl->gl_aspace->i_mapping); }/**
fs/gfs2/meta_io.c +1 -1
@@ -104,7 +104,7 @@ void gfs2_meta_inval(struct gfs2_glock *truncate_inode_pages (mapping,0 );atomic_dec (&aspace->i_writecount); -gfs2_assert_withdraw (sdp, !mapping->nrpages); +gfs2_assert_withdraw (sdp, !mapping_nrpages (mapping)); }/**
fs/hugetlbfs/inode.c +1 -1
@@ -214,7 +214,7 @@ static void truncate_hugepages(struct in }huge_pagevec_release (&pvec); } -BUG_ON (!lstart && mapping->nrpages); +BUG_ON (!lstart &&mapping_nrpages (mapping));hugetlb_unreserve_pages (inode, start, freed); }
fs/jffs/inode-v23.c +1 -1
@@ -1352,7 +1352,7 @@ jffs_create(struct inode *dir, struct de inode->i_op = &jffs_file_inode_operations; inode->i_fop = &jffs_file_operations; inode->i_mapping->a_ops = &jffs_address_operations; - inode->i_mapping->nrpages =0 ; +mapping_nrpages_init (inode->i_mapping);d_instantiate (dentry, inode); jffs_create_end:
fs/jffs2/dir.c +2 -2
@@ -206,7 +206,7 @@ static int jffs2_create(struct inode *di inode->i_op = &jffs2_file_inode_operations; inode->i_fop = &jffs2_file_operations; inode->i_mapping->a_ops = &jffs2_file_address_operations; - inode->i_mapping->nrpages =0 ; +mapping_nrpages_init (inode->i_mapping); f =JFFS2_INODE_INFO (inode); dir_f =JFFS2_INODE_INFO (dir_i);
@@ -230,7 +230,7 @@ static int jffs2_create(struct inode *did_instantiate (dentry, inode);D1 (printk (KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n" , - inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages)); + inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink,mapping_nrpages (inode->i_mapping)));return 0 ; fail:
fs/jffs2/fs.c +1 -1
@@ -293,7 +293,7 @@ void jffs2_read_inode (struct inode *ino inode->i_op = &jffs2_file_inode_operations; inode->i_fop = &jffs2_file_operations; inode->i_mapping->a_ops = &jffs2_file_address_operations; - inode->i_mapping->nrpages =0 ; +mapping_nrpages_init (inode->i_mapping);break ;case S_IFBLK :
fs/libfs.c +1 -1
@@ -16,7 +16,7 @@ int simple_getattr(struct vfsmount *mnt, {struct inode *inode = dentry->d_inode;generic_fillattr (inode, stat); - stat->blocks = inode->i_mapping->nrpages << (PAGE_CACHE_SHIFT -9 ); + stat->blocks =mapping_nrpages (inode->i_mapping) << (PAGE_CACHE_SHIFT -9 );return 0 ; }
fs/nfs/inode.c +3 -3
@@ -93,7 +93,7 @@ int nfs_sync_mapping(struct address_spac {int ret; -if (mapping->nrpages ==0 ) +if (mapping_nrpages (mapping) ==0 )return 0 ;unmap_mapping_range (mapping,0 ,0 ,0 ); ret =filemap_write_and_wait (mapping);
@@ -133,7 +133,7 @@ void nfs_zap_caches(struct inode *inode)void nfs_zap_mapping (struct inode *inode,struct address_space *mapping) { -if (mapping->nrpages !=0 ) { +if (mapping_nrpages (mapping) !=0 ) {spin_lock (&inode->i_lock);NFS_I (inode)->cache_validity |=NFS_INO_INVALID_DATA ;spin_unlock (&inode->i_lock);
@@ -684,7 +684,7 @@ int nfs_revalidate_mapping(struct inode goto out;if (nfsi->cache_validity &NFS_INO_INVALID_DATA ) { -if (mapping->nrpages !=0 ) { +if (mapping_nrpages (mapping) !=0 ) {if (S_ISREG (inode->i_mode)) { ret =nfs_sync_mapping (mapping);if (ret <0 )
fs/xfs/linux-2.6/xfs_vnode.h +1 -1
@@ -548,7 +548,7 @@ static inline void vn_atime_to_time_t(bh* Some useful predicates. */ #define VN_MAPPED (vp) mapping_mapped(vn_to_inode(vp)->i_mapping) -#define VN_CACHED (vp) (vn_to_inode(vp)->i_mapping->nrpages) +#define VN_CACHED (vp) mapping_nrpages(vn_to_inode(vp)->i_mapping)#define VN_DIRTY (vp) mapping_tagged(vn_to_inode(vp)->i_mapping, \ PAGECACHE_TAG_DIRTY)#define VN_TRUNC (vp) ((vp)->v_flag & VTRUNCATED)
include/linux/fs.h +21 -1
@@ -436,7 +436,7 @@ struct address_space {struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */ spinlock_t i_mmap_lock;/* protect tree, count, list */ unsigned int truncate_count;/* Cover race condition with truncate */ -unsigned long nrpages;/* number of total pages */ +unsigned long __nrpages;/* number of total pages */ pgoff_t writeback_index;/* writeback starts here */ const struct address_space_operations *a_ops;/* methods */ unsigned long flags;/* error bits/gfp mask */
@@ -451,6 +451,26 @@ struct address_space {* of struct page's "mapping" pointer be used for PAGE_MAPPING_ANON. */ +static inline void mapping_nrpages_init (struct address_space *mapping) +{ + mapping->__nrpages =0 ; +} + +static inline unsigned long mapping_nrpages (struct address_space *mapping) +{ +return mapping->__nrpages; +} + +static inline void mapping_nrpages_inc (struct address_space *mapping) +{ + mapping->__nrpages++; +} + +static inline void mapping_nrpages_dec (struct address_space *mapping) +{ + mapping->__nrpages--; +} +struct block_device {dev_t bd_dev;/* not a kdev_t - it's a search key */ struct inode * bd_inode;/* will die */
ipc/shm.c +2 -2
@@ -499,11 +499,11 @@ static void shm_get_stat(struct ipc_nameif (is_file_hugepages (shp->shm_file)) {struct address_space *mapping = inode->i_mapping; - *rss += (HPAGE_SIZE /PAGE_SIZE )*mapping->nrpages; + *rss += (HPAGE_SIZE /PAGE_SIZE )*mapping_nrpages (mapping); }else {struct shmem_inode_info *info =SHMEM_I (inode);spin_lock (&info->lock); - *rss += inode->i_mapping->nrpages; + *rss +=mapping_nrpages (inode->i_mapping); *swp += info->swapped;spin_unlock (&info->lock); }
mm/filemap.c +6 -6
@@ -118,7 +118,7 @@ void __remove_from_page_cache(struct pagradix_tree_delete (&mapping->page_tree, page->index); page->mapping =NULL ; - mapping->nrpages--; +mapping_nrpages_dec (mapping);__dec_zone_page_state (page,NR_FILE_PAGES ); }
@@ -190,7 +190,7 @@ int __filemap_fdatawrite_range(struct adint ret;struct writeback_control wbc = { .sync_mode = sync_mode, - .nr_to_write = mapping->nrpages *2 , + .nr_to_write =mapping_nrpages (mapping) *2 , .range_start = start, .range_end = end, };
@@ -372,7 +372,7 @@ int filemap_write_and_wait(struct addres {int err =0 ; -if (mapping->nrpages) { +if (mapping_nrpages (mapping)) { err =filemap_fdatawrite (mapping);/* * Even if the above returned error, the pages may be
@@ -406,7 +406,7 @@ int filemap_write_and_wait_range(struct {int err =0 ; -if (mapping->nrpages) { +if (mapping_nrpages (mapping)) { err =__filemap_fdatawrite_range (mapping, lstart, lend,WB_SYNC_ALL );/* See comment of filemap_write_and_wait() */
@@ -448,7 +448,7 @@ int add_to_page_cache(struct page *page,SetPageLocked (page); page->mapping = mapping; page->index = offset; - mapping->nrpages++; +mapping_nrpages_inc (mapping);__inc_zone_page_state (page,NR_FILE_PAGES ); }spin_unlock_irq (&mapping->tree_lock);
@@ -2469,7 +2469,7 @@ generic_file_direct_IO(int rw, struct kiif (retval ==0 ) { retval = mapping->a_ops->direct_IO (rw, iocb, iov, offset, nr_segs); -if (rw ==WRITE && mapping->nrpages) { +if (rw ==WRITE &&mapping_nrpages (mapping)) {pgoff_t end = (offset + write_len -1 ) >>PAGE_CACHE_SHIFT ; int err = invalidate_inode_pages2_range(mapping,
mm/shmem.c +4 -4
@@ -211,8 +211,8 @@ static void shmem_free_blocks(struct ino* We have to calculate the free blocks since the mm can drop * undirtied hole pages behind our back. * -* But normally info->alloced == inode->i_mapping->nrpages + info->swapped -* So mm freed is info->alloced - (inode->i_mapping->nrpages + info->swapped) +* But normally info->alloced == mapping_nrpages(inode->i_mapping) + info->swapped +* So mm freed is info->alloced - (mapping_nrpages(inode->i_mapping) + info->swapped) * * It has to be called with the spinlock held. */
@@ -221,7 +221,7 @@ static void shmem_recalc_inode(struct instruct shmem_inode_info *info =SHMEM_I (inode);long freed; - freed = info->alloced - info->swapped - inode->i_mapping->nrpages; + freed = info->alloced - info->swapped -mapping_nrpages (inode->i_mapping);if (freed >0 ) { info->alloced -= freed;shmem_unacct_blocks (info->flags, freed);
@@ -602,7 +602,7 @@ static void shmem_truncate_range(struct done1:shmem_dir_unmap (dir); done2: -if (inode->i_mapping->nrpages && (info->flags &SHMEM_PAGEIN )) { +if (mapping_nrpages (inode->i_mapping) && (info->flags &SHMEM_PAGEIN )) {/* * Call truncate_inode_pages again: racing shmem_unuse_inode * may have swizzled a page in from swap since vmtruncate or
mm/swap_state.c +2 -2
@@ -87,7 +87,7 @@ static int __add_to_swap_cache(struct paSetPageLocked (page);SetPageSwapCache (page);set_page_private (page, entry.val); - total_swapcache_pages++; +mapping_nrpages_inc (&swapper_space);__inc_zone_page_state (page,NR_FILE_PAGES ); }spin_unlock_irq (&swapper_space.tree_lock);
@@ -133,7 +133,7 @@ void __delete_from_swap_cache(struct pagradix_tree_delete (&swapper_space.page_tree,page_private (page));set_page_private (page,0 );ClearPageSwapCache (page); - total_swapcache_pages--; +mapping_nrpages_dec (&swapper_space);__dec_zone_page_state (page,NR_FILE_PAGES );INC_CACHE_INFO (del_total); }
mm/truncate.c +1 -1
@@ -134,7 +134,7 @@ void truncate_inode_pages_range(struct apgoff_t next;int i; -if (mapping->nrpages ==0 ) +if (mapping_nrpages (mapping) ==0 )return ;BUG_ON ((lend & (PAGE_CACHE_SIZE -1 )) != (PAGE_CACHE_SIZE -1 ));
include/linux/swap.h +1 -1
@@ -224,7 +224,7 @@ extern int end_swap_bio_read(struct bio/* linux/mm/swap_state.c */ extern struct address_space swapper_space; -#define total_swapcache_pages swapper_space.nrpages +#define total_swapcache_pages mapping_nrpages(&swapper_space)extern void show_swap_cache_info (void );extern int add_to_swap (struct page *,gfp_t );extern void __delete_from_swap_cache (struct page *);
fs/inode.c +5 -5
@@ -246,7 +246,7 @@ void clear_inode(struct inode *inode)might_sleep ();invalidate_inode_buffers (inode); -BUG_ON (inode->i_data.nrpages); +BUG_ON (mapping_nrpages (&inode->i_data));BUG_ON (!(inode->i_state &I_FREEING ));BUG_ON (inode->i_state &I_CLEAR );wait_on_inode (inode);
@@ -279,7 +279,7 @@ static void dispose_list(struct list_hea inode = list_entry(head->next, struct inode, i_list);list_del (&inode->i_list); -if (inode->i_data.nrpages) +if (mapping_nrpages (&inode->i_data))truncate_inode_pages (&inode->i_data,0 );clear_inode (inode);
@@ -371,7 +371,7 @@ static int can_unuse(struct inode *inodereturn 0 ;if (atomic_read (&inode->i_count))return 0 ; -if (inode->i_data.nrpages) +if (mapping_nrpages (&inode->i_data))return 0 ;return 1 ; }
@@ -410,7 +410,7 @@ static void prune_icache(int nr_to_scan)list_move (&inode->i_list, &inode_unused);continue ; } -if (inode_has_buffers (inode) || inode->i_data.nrpages) { +if (inode_has_buffers (inode) ||mapping_nrpages (&inode->i_data)) {__iget (inode);spin_unlock (&inode_lock);if (remove_inode_buffers (inode))
@@ -1057,7 +1057,7 @@ static void generic_forget_inode(struct inode->i_state |=I_FREEING ; inodes_stat.nr_inodes--;spin_unlock (&inode_lock); -if (inode->i_data.nrpages) +if (mapping_nrpages (&inode->i_data))truncate_inode_pages (&inode->i_data,0 );clear_inode (inode);wake_up_inode (inode);
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 15/16 mm: lock_page_ref
In reply to Peter ZijlstraChange the PG_nonewref operations into locking primitives and place them so that they provide page level serialization with regard to the page_tree operations. (basically replace the tree_lock with a per page lock).
The normal page lock has sufficiently different (and overlapping) scope and protection rules that this second lock is needed.
--- fs/buffer.c | 6 ++- include/linux/pagemap.h | 76 ++++++++++++++++++++++++++++++++---------------- mm/filemap.c | 14 +++++--- mm/migrate.c | 12 +++---- mm/page-writeback.c | 27 +++++++++++------ mm/swap_state.c | 14 +++++--- mm/swapfile.c | 6 ++- mm/truncate.c | 9 +++-- mm/vmscan.c | 14 ++++---- 9 files changed, 112 insertions(+), 66 deletions(-)
include/linux/pagemap.h +51 -25
@@ -75,55 +75,81 @@ static inline wait_queue_head_t *page_waextern int __sleep_on_page (void *);#ifndef CONFIG_PREEMPT_RT -static inline void set_page_no_new_refs (struct page *page) +static inline void lock_page_ref (struct page *page) { -VM_BUG_ON (PageNoNewRefs (page));preempt_disable (); -SetPageNoNewRefs (page); +bit_spin_lock (PG_nonewrefs, &page->flags);smp_wmb (); } -static inline void end_page_no_new_refs (struct page *page) +static inline void unlock_page_ref (struct page *page) { -VM_BUG_ON (!PageNoNewRefs (page)); -smp_wmb (); -ClearPageNoNewRefs (page); +bit_spin_unlock (PG_nonewrefs, &page->flags);preempt_enable (); } -static inline void wait_on_new_refs (struct page *page) +static inline void wait_on_unlock_page_ref (struct page *page) { -while (unlikely (PageNoNewRefs (page))) +while (unlikely (test_bit (PG_nonewrefs, &page->flags)))cpu_relax (); }#else -static inline void set_page_no_new_refs (struct page *page) +/* +* open coded sleeping bit lock, yay! +*/ +static inline void wait_on_unlock_page_ref (struct page *page) { -VM_BUG_ON (PageNoNewRefs (page)); -SetPageNoNewRefs (page); +might_sleep (); +if (unlikely (PageNoNewRefs (page))) { +DEFINE_WAIT_BIT (wait, &page->flags, PG_nonewrefs); +__wait_on_bit (page_waitqueue (page), &wait, __sleep_on_page, +TASK_UNINTERRUPTIBLE ); + } +} + +static inline void lock_page_ref (struct page *page) +{ +while (test_and_set_bit (PG_nonewrefs, &page->flags)) +wait_on_unlock_page_ref (page); +__acquire (bitlock);smp_wmb (); } -static inline void end_page_no_new_refs (struct page *page) +static inline void unlock_page_ref (struct page *page) {VM_BUG_ON (!PageNoNewRefs (page)); -smp_wmb (); +smp_mb__before_clear_bit ();ClearPageNoNewRefs (page);smp_mb__after_clear_bit ();__wake_up_bit (page_waitqueue (page), &page->flags, PG_nonewrefs); -} - -static inline void wait_on_new_refs (struct page *page) -{ -might_sleep (); -if (unlikely (PageNoNewRefs (page))) { -DEFINE_WAIT_BIT (wait, &page->flags, PG_nonewrefs); -__wait_on_bit (page_waitqueue (page), &wait, __sleep_on_page, -TASK_UNINTERRUPTIBLE ); - } +__release (bitlock); }#endif +#define lock_page_ref_irq (page) \ +do { \ + local_irq_disable_nort(); \ + lock_page_ref(page); \ +} while (0) + +#define unlock_page_ref_irq (page) \ +do { \ + unlock_page_ref(page); \ + local_irq_enable_nort(); \ +} while (0) + +#define lock_page_ref_irqsave (page, flags) \ +do { \ + local_irq_save_nort(flags); \ + lock_page_ref(page); \ +} while (0) + +#define unlock_page_ref_irqrestore (page, flags) \ +do { \ + unlock_page_ref(page); \ + local_irq_restore_nort(flags); \ +} while (0) +/* * speculatively take a reference to a page. * If the page is free (_count == 0), then _count is untouched, and 0
@@ -199,7 +225,7 @@ static inline int page_cache_get_specula* page refcount has been raised. See below comment. */ -wait_on_new_refs (page); +wait_on_unlock_page_ref (page);/* * smp_rmb is to ensure the load of page->flags (for PageNoNewRefs())
mm/filemap.c +8 -6
@@ -128,9 +128,11 @@ void remove_from_page_cache(struct pageBUG_ON (!PageLocked (page)); -spin_lock_irq (&mapping->tree_lock); +lock_page_ref_irq (page); +spin_lock (&mapping->tree_lock);__remove_from_page_cache (page); -spin_unlock_irq (&mapping->tree_lock); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irq (page); }static int sync_page (void *word)
@@ -440,8 +442,8 @@ int add_to_page_cache(struct page *page,int error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM);if (error ==0 ) { -set_page_no_new_refs (page); -spin_lock_irq (&mapping->tree_lock); +lock_page_ref_irq (page); +spin_lock (&mapping->tree_lock); error =radix_tree_insert (&mapping->page_tree, offset, page);if (!error) {page_cache_get (page);
@@ -451,8 +453,8 @@ int add_to_page_cache(struct page *page,mapping_nrpages_inc (mapping);__inc_zone_page_state (page,NR_FILE_PAGES ); } -spin_unlock_irq (&mapping->tree_lock); -end_page_no_new_refs (page); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irq (page);radix_tree_preload_end (); }return error;
mm/migrate.c +6 -6
@@ -303,16 +303,16 @@ static int migrate_page_move_mapping(strreturn 0 ; } -set_page_no_new_refs (page); -spin_lock_irq (&mapping->tree_lock); +lock_page_ref_irq (page); +spin_lock (&mapping->tree_lock); pslot =radix_tree_lookup_slot (&mapping->page_tree,page_index (page));if (page_count (page) !=2 + !!PagePrivate (page) || (struct page *)radix_tree_deref_slot (pslot) != page) { -spin_unlock_irq (&mapping->tree_lock); -end_page_no_new_refs (page); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irq (page);return -EAGAIN ; }
@@ -329,8 +329,8 @@ static int migrate_page_move_mapping(strradix_tree_replace_slot (pslot, newpage); page->mapping =NULL ; -spin_unlock_irq (&mapping->tree_lock); -end_page_no_new_refs (page); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irq (page);/* * Drop cache reference from old page.
mm/swap_state.c +8 -6
@@ -78,8 +78,8 @@ static int __add_to_swap_cache(struct paBUG_ON (PagePrivate (page)); error =radix_tree_preload (gfp_mask);if (!error) { -set_page_no_new_refs (page); -spin_lock_irq (&swapper_space.tree_lock); +lock_page_ref_irq (page); +spin_lock (&swapper_space.tree_lock); error =radix_tree_insert (&swapper_space.page_tree, entry.val, page);if (!error) {
@@ -90,8 +90,8 @@ static int __add_to_swap_cache(struct pamapping_nrpages_inc (&swapper_space);__inc_zone_page_state (page,NR_FILE_PAGES ); } -spin_unlock_irq (&swapper_space.tree_lock); -end_page_no_new_refs (page); +spin_unlock (&swapper_space.tree_lock); +unlock_page_ref_irq (page);radix_tree_preload_end (); }return error;
@@ -202,9 +202,11 @@ void delete_from_swap_cache(struct page entry.val =page_private (page); -spin_lock_irq (&swapper_space.tree_lock); +lock_page_ref_irq (page); +spin_lock (&swapper_space.tree_lock);__delete_from_swap_cache (page); -spin_unlock_irq (&swapper_space.tree_lock); +spin_unlock (&swapper_space.tree_lock); +unlock_page_ref_irq (page);swap_free (entry);page_cache_release (page);
mm/vmscan.c +7 -7
@@ -390,8 +390,8 @@ int remove_mapping(struct address_spaceBUG_ON (!PageLocked (page));BUG_ON (mapping !=page_mapping (page)); -set_page_no_new_refs (page); -spin_lock_irq (&mapping->tree_lock); +lock_page_ref_irq (page); +spin_lock (&mapping->tree_lock);/* * The non racy check for a busy page. *
@@ -426,22 +426,22 @@ int remove_mapping(struct address_spaceif (PageSwapCache (page)) {swp_entry_t swap = { .val =page_private (page) };__delete_from_swap_cache (page); -spin_unlock_irq (&mapping->tree_lock); +spin_unlock (&mapping->tree_lock);swap_free (swap); goto free_it; }__remove_from_page_cache (page); -spin_unlock_irq (&mapping->tree_lock); +spin_unlock (&mapping->tree_lock); free_it: -end_page_no_new_refs (page); +unlock_page_ref_irq (page);__put_page (page);/* The pagecache ref */ return 1 ; cannot_free: -spin_unlock_irq (&mapping->tree_lock); -end_page_no_new_refs (page); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irq (page);return 0 ; }
fs/buffer.c +4 -2
@@ -719,7 +719,8 @@ int __set_page_dirty_buffers(struct pagespin_unlock (&mapping->private_lock);if (!TestSetPageDirty (page)) { -spin_lock_irq (&mapping->tree_lock); +lock_page_ref_irq (page); +spin_lock (&mapping->tree_lock);if (page->mapping) {/* Race with truncate? */ if (mapping_cap_account_dirty (mapping))__inc_zone_page_state (page,NR_FILE_DIRTY );
@@ -727,7 +728,8 @@ int __set_page_dirty_buffers(struct pagepage_index (page), PAGECACHE_TAG_DIRTY); } -spin_unlock_irq (&mapping->tree_lock); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irq (page);__mark_inode_dirty (mapping->host,I_DIRTY_PAGES );return 1 ; }
mm/page-writeback.c +18 -9
@@ -762,7 +762,8 @@ int __set_page_dirty_nobuffers(struct pastruct address_space *mapping2;if (mapping) { -spin_lock_irq (&mapping->tree_lock); +lock_page_ref_irq (page); +spin_lock (&mapping->tree_lock); mapping2 =page_mapping (page);if (mapping2) {/* Race with truncate? */ BUG_ON (mapping2 != mapping);
@@ -772,7 +773,8 @@ int __set_page_dirty_nobuffers(struct paradix_tree_tag_set (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); } -spin_unlock_irq (&mapping->tree_lock); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irq (page);if (mapping->host) {/* !PageAnon && !swapper_space */ __mark_inode_dirty(mapping->host,
@@ -852,12 +854,14 @@ int test_clear_page_dirty(struct page *punsigned long flags;if (mapping) { -spin_lock_irqsave (&mapping->tree_lock, flags); +lock_page_ref_irqsave (page, flags); +spin_lock (&mapping->tree_lock);if (TestClearPageDirty (page)) {radix_tree_tag_clear (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); -spin_unlock_irqrestore (&mapping->tree_lock, flags); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irqrestore (page, flags);/* * We can continue to use `mapping' here because the * page is locked, which pins the address_space
@@ -868,7 +872,8 @@ int test_clear_page_dirty(struct page *p }return 1 ; } -spin_unlock_irqrestore (&mapping->tree_lock, flags); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irqrestore (page, flags);return 0 ; }return TestClearPageDirty (page);
@@ -915,13 +920,15 @@ int test_clear_page_writeback(struct pagif (mapping) {unsigned long flags; -spin_lock_irqsave (&mapping->tree_lock, flags); +lock_page_ref_irqsave (page, flags); +spin_lock (&mapping->tree_lock); ret =TestClearPageWriteback (page);if (ret)radix_tree_tag_clear (&mapping->page_tree,page_index (page),PAGECACHE_TAG_WRITEBACK ); -spin_unlock_irqrestore (&mapping->tree_lock, flags); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irqrestore (page, flags); }else { ret =TestClearPageWriteback (page); }
@@ -936,7 +943,8 @@ int test_set_page_writeback(struct pageif (mapping) {unsigned long flags; -spin_lock_irqsave (&mapping->tree_lock, flags); +lock_page_ref_irqsave (page, flags); +spin_lock (&mapping->tree_lock); ret =TestSetPageWriteback (page);if (!ret) radix_tree_tag_set(&mapping->page_tree,
@@ -946,7 +954,8 @@ int test_set_page_writeback(struct pageradix_tree_tag_clear (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); -spin_unlock_irqrestore (&mapping->tree_lock, flags); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irqrestore (page, flags); }else { ret =TestSetPageWriteback (page); }
mm/swapfile.c +4 -2
@@ -367,13 +367,15 @@ int remove_exclusive_swap_page(struct pa retval =0 ;if (p->swap_map[swp_offset (entry)] ==1 ) {/* Recheck the page count with the swapcache lock held.. */ -spin_lock_irq (&swapper_space.tree_lock); +lock_page_ref_irq (page); +spin_lock (&swapper_space.tree_lock);if ((page_count (page) ==2 ) && !PageWriteback (page)) {__delete_from_swap_cache (page);SetPageDirty (page); retval =1 ; } -spin_unlock_irq (&swapper_space.tree_lock); +spin_unlock (&swapper_space.tree_lock); +unlock_page_ref_irq (page); }spin_unlock (&swap_lock);
mm/truncate.c +6 -3
@@ -304,18 +304,21 @@ invalidate_complete_page2(struct addressif (PagePrivate (page) && !try_to_release_page (page,GFP_KERNEL ))return 0 ; -spin_lock_irq (&mapping->tree_lock); +lock_page_ref_irq (page); +spin_lock (&mapping->tree_lock);if (PageDirty (page)) goto failed;BUG_ON (PagePrivate (page));__remove_from_page_cache (page); -spin_unlock_irq (&mapping->tree_lock); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irq (page);ClearPageUptodate (page);page_cache_release (page);/* pagecache ref */ return 1 ; failed: -spin_unlock_irq (&mapping->tree_lock); +spin_unlock (&mapping->tree_lock); +unlock_page_ref_irq (page);return 0 ; }
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 16/16 mm: concurrent pagecache write side
In reply to Peter ZijlstraRemove the tree_lock, change address_space::nrpages to atomic_ulong_t because its not protected any longer and use the concurrent radix tree API to protect the modifying radix tree operations.
--- fs/buffer.c | 6 ++++-- fs/inode.c | 1 - include/linux/fs.h | 11 +++++------ mm/filemap.c | 11 +++++++---- mm/migrate.c | 9 +++++---- mm/page-writeback.c | 35 +++++++++++++++++++++++------------ mm/swap_state.c | 13 ++++++++----- mm/swapfile.c | 2 -- mm/truncate.c | 3 --- mm/vmscan.c | 4 ---- 10 files changed, 52 insertions(+), 43 deletions(-)
fs/buffer.c +4 -2
@@ -720,15 +720,17 @@ int __set_page_dirty_buffers(struct pageif (!TestSetPageDirty (page)) {lock_page_ref_irq (page); -spin_lock (&mapping->tree_lock);if (page->mapping) {/* Race with truncate? */ +DECLARE_RADIX_TREE_CONTEXT (ctx, &mapping->page_tree); +if (mapping_cap_account_dirty (mapping))__inc_zone_page_state (page,NR_FILE_DIRTY ); +radix_tree_lock (&ctx);radix_tree_tag_set (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); +radix_tree_unlock (&ctx); } -spin_unlock (&mapping->tree_lock);unlock_page_ref_irq (page);__mark_inode_dirty (mapping->host,I_DIRTY_PAGES );return 1 ;
fs/inode.c +0 -1
@@ -193,7 +193,6 @@ void inode_init_once(struct inode *inodemutex_init (&inode->i_mutex);init_rwsem (&inode->i_alloc_sem);INIT_RADIX_TREE (&inode->i_data.page_tree,GFP_ATOMIC ); -spin_lock_init (&inode->i_data.tree_lock);spin_lock_init (&inode->i_data.i_mmap_lock);INIT_LIST_HEAD (&inode->i_data.private_list);spin_lock_init (&inode->i_data.private_lock);
include/linux/fs.h +5 -6
@@ -430,13 +430,12 @@ struct backing_dev_info;struct address_space {struct inode *host;/* owner: inode, block_device */ struct radix_tree_root page_tree;/* radix tree of all pages */ -spinlock_t tree_lock;/* and rwlock protecting it */ unsigned int i_mmap_writable;/* count VM_SHARED mappings */ struct prio_tree_root i_mmap;/* tree of private and shared mappings */ struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */ spinlock_t i_mmap_lock;/* protect tree, count, list */ unsigned int truncate_count;/* Cover race condition with truncate */ -unsigned long __nrpages;/* number of total pages */ +atomic_ulong_t __nrpages;/* number of total pages */ pgoff_t writeback_index;/* writeback starts here */ const struct address_space_operations *a_ops;/* methods */ unsigned long flags;/* error bits/gfp mask */
@@ -453,22 +452,22 @@ struct address_space {static inline void mapping_nrpages_init (struct address_space *mapping) { - mapping->__nrpages =0 ; + mapping->__nrpages = (atomic_ulong_t )ATOMIC_ULONG_INIT (0 ); }static inline unsigned long mapping_nrpages (struct address_space *mapping) { -return mapping->__nrpages; +return atomic_ulong_read (&mapping->__nrpages); }static inline void mapping_nrpages_inc (struct address_space *mapping) { - mapping->__nrpages++; +atomic_ulong_inc (&mapping->__nrpages); }static inline void mapping_nrpages_dec (struct address_space *mapping) { - mapping->__nrpages--; +atomic_ulong_dec (&mapping->__nrpages); }struct block_device {
mm/filemap.c +7 -4
@@ -115,8 +115,11 @@ generic_file_direct_IO(int rw, struct kivoid __remove_from_page_cache (struct page *page) {struct address_space *mapping = page->mapping; +DECLARE_RADIX_TREE_CONTEXT (ctx, &mapping->page_tree); +radix_tree_lock (&ctx);radix_tree_delete (&mapping->page_tree, page->index); +radix_tree_unlock (&ctx); page->mapping =NULL ;mapping_nrpages_dec (mapping);__dec_zone_page_state (page,NR_FILE_PAGES );
@@ -129,9 +132,7 @@ void remove_from_page_cache(struct pageBUG_ON (!PageLocked (page));lock_page_ref_irq (page); -spin_lock (&mapping->tree_lock);__remove_from_page_cache (page); -spin_unlock (&mapping->tree_lock);unlock_page_ref_irq (page); }
@@ -442,9 +443,12 @@ int add_to_page_cache(struct page *page,int error =radix_tree_preload (gfp_mask & ~__GFP_HIGHMEM);if (error ==0 ) { +DECLARE_RADIX_TREE_CONTEXT (ctx, &mapping->page_tree); +lock_page_ref_irq (page); -spin_lock (&mapping->tree_lock); +radix_tree_lock (&ctx); error =radix_tree_insert (&mapping->page_tree, offset, page); +radix_tree_unlock (&ctx);if (!error) {page_cache_get (page);SetPageLocked (page);
@@ -453,7 +457,6 @@ int add_to_page_cache(struct page *page,mapping_nrpages_inc (mapping);__inc_zone_page_state (page,NR_FILE_PAGES ); } -spin_unlock (&mapping->tree_lock);unlock_page_ref_irq (page);radix_tree_preload_end (); }
mm/migrate.c +5 -4
@@ -295,6 +295,7 @@ static int migrate_page_move_mapping(str struct page *newpage, struct page *page) { void **pslot; +struct radix_tree_context ctx;if (!mapping) {/* Anonymous page */
@@ -303,15 +304,15 @@ static int migrate_page_move_mapping(strreturn 0 ; } +init_radix_tree_context (&context, &mapping->page_tree);lock_page_ref_irq (page); -spin_lock (&mapping->tree_lock); - +radix_tree_lock (&ctx); pslot =radix_tree_lookup_slot (&mapping->page_tree,page_index (page));if (page_count (page) !=2 + !!PagePrivate (page) || (struct page *)radix_tree_deref_slot (pslot) != page) { -spin_unlock (&mapping->tree_lock); +radix_tree_unlock (&ctx);unlock_page_ref_irq (page);return -EAGAIN ; }
@@ -329,7 +330,7 @@ static int migrate_page_move_mapping(strradix_tree_replace_slot (pslot, newpage); page->mapping =NULL ; -spin_unlock (&mapping->tree_lock); +radix_tree_unlock (&ctx);unlock_page_ref_irq (page);/*
mm/page-writeback.c +23 -12
@@ -763,17 +763,19 @@ int __set_page_dirty_nobuffers(struct paif (mapping) {lock_page_ref_irq (page); -spin_lock (&mapping->tree_lock); mapping2 =page_mapping (page);if (mapping2) {/* Race with truncate? */ +DECLARE_RADIX_TREE_CONTEXT (ctx, &mapping->page_tree); +BUG_ON (mapping2 != mapping);if (mapping_cap_account_dirty (mapping))__inc_zone_page_state (page,NR_FILE_DIRTY ); +radix_tree_lock (&ctx);radix_tree_tag_set (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); +radix_tree_unlock (&ctx); } -spin_unlock (&mapping->tree_lock);unlock_page_ref_irq (page);if (mapping->host) {/* !PageAnon && !swapper_space */
@@ -855,12 +857,14 @@ int test_clear_page_dirty(struct page *pif (mapping) {lock_page_ref_irqsave (page, flags); -spin_lock (&mapping->tree_lock);if (TestClearPageDirty (page)) { +DECLARE_RADIX_TREE_CONTEXT (ctx, &mapping->page_tree); + +radix_tree_lock (&ctx);radix_tree_tag_clear (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); -spin_unlock (&mapping->tree_lock); +radix_tree_unlock (&ctx);unlock_page_ref_irqrestore (page, flags);/* * We can continue to use `mapping' here because the
@@ -872,7 +876,6 @@ int test_clear_page_dirty(struct page *p }return 1 ; } -spin_unlock (&mapping->tree_lock);unlock_page_ref_irqrestore (page, flags);return 0 ; }
@@ -921,13 +924,16 @@ int test_clear_page_writeback(struct pagunsigned long flags;lock_page_ref_irqsave (page, flags); -spin_lock (&mapping->tree_lock); ret =TestClearPageWriteback (page); -if (ret) +if (ret) { +DECLARE_RADIX_TREE_CONTEXT (ctx, &mapping->page_tree); + +radix_tree_lock (&ctx);radix_tree_tag_clear (&mapping->page_tree,page_index (page),PAGECACHE_TAG_WRITEBACK ); -spin_unlock (&mapping->tree_lock); +radix_tree_unlock (&ctx); + }unlock_page_ref_irqrestore (page, flags); }else { ret =TestClearPageWriteback (page);
@@ -942,19 +948,24 @@ int test_set_page_writeback(struct pageif (mapping) {unsigned long flags; +DECLARE_RADIX_TREE_CONTEXT (ctx, &mapping->page_tree);lock_page_ref_irqsave (page, flags); -spin_lock (&mapping->tree_lock); ret =TestSetPageWriteback (page); -if (!ret) +if (!ret) { +radix_tree_lock (&ctx);radix_tree_tag_set (&mapping->page_tree,page_index (page),PAGECACHE_TAG_WRITEBACK ); -if (!PageDirty (page)) +radix_tree_unlock (&ctx); + } +if (!PageDirty (page)) { +radix_tree_lock (&ctx);radix_tree_tag_clear (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY ); -spin_unlock (&mapping->tree_lock); +radix_tree_unlock (&ctx); + }unlock_page_ref_irqrestore (page, flags); }else { ret =TestSetPageWriteback (page);
mm/swap_state.c +8 -5
@@ -38,7 +38,6 @@ static struct backing_dev_info swap_backstruct address_space swapper_space = { .page_tree =RADIX_TREE_INIT (GFP_ATOMIC |__GFP_NOWARN), - .tree_lock =__SPIN_LOCK_UNLOCKED (swapper_space.tree_lock), .a_ops = &swap_aops, .i_mmap_nonlinear =LIST_HEAD_INIT (swapper_space.i_mmap_nonlinear), .backing_dev_info = &swap_backing_dev_info,
@@ -78,10 +77,13 @@ static int __add_to_swap_cache(struct paBUG_ON (PagePrivate (page)); error =radix_tree_preload (gfp_mask);if (!error) { +DECLARE_RADIX_TREE_CONTEXT (ctx, &swapper_space.page_tree); +lock_page_ref_irq (page); -spin_lock (&swapper_space.tree_lock); +radix_tree_lock (&ctx); error =radix_tree_insert (&swapper_space.page_tree, entry.val, page); +radix_tree_unlock (&ctx);if (!error) {page_cache_get (page);SetPageLocked (page);
@@ -90,7 +92,6 @@ static int __add_to_swap_cache(struct pamapping_nrpages_inc (&swapper_space);__inc_zone_page_state (page,NR_FILE_PAGES ); } -spin_unlock (&swapper_space.tree_lock);unlock_page_ref_irq (page);radix_tree_preload_end (); }
@@ -125,12 +126,16 @@ static int add_to_swap_cache(struct page*/ void __delete_from_swap_cache (struct page *page) { +DECLARE_RADIX_TREE_CONTEXT (ctx, &swapper_space.page_tree); +BUG_ON (!PageLocked (page));BUG_ON (!PageSwapCache (page));BUG_ON (PageWriteback (page));BUG_ON (PagePrivate (page)); +radix_tree_lock (&ctx);radix_tree_delete (&swapper_space.page_tree,page_private (page)); +radix_tree_unlock (&ctx);set_page_private (page,0 );ClearPageSwapCache (page);mapping_nrpages_dec (&swapper_space);
@@ -203,9 +208,7 @@ void delete_from_swap_cache(struct page entry.val =page_private (page);lock_page_ref_irq (page); -spin_lock (&swapper_space.tree_lock);__delete_from_swap_cache (page); -spin_unlock (&swapper_space.tree_lock);unlock_page_ref_irq (page);swap_free (entry);
mm/swapfile.c +0 -2
@@ -368,13 +368,11 @@ int remove_exclusive_swap_page(struct paif (p->swap_map[swp_offset (entry)] ==1 ) {/* Recheck the page count with the swapcache lock held.. */ lock_page_ref_irq (page); -spin_lock (&swapper_space.tree_lock);if ((page_count (page) ==2 ) && !PageWriteback (page)) {__delete_from_swap_cache (page);SetPageDirty (page); retval =1 ; } -spin_unlock (&swapper_space.tree_lock);unlock_page_ref_irq (page); }spin_unlock (&swap_lock);
mm/truncate.c +0 -3
@@ -305,19 +305,16 @@ invalidate_complete_page2(struct addressreturn 0 ;lock_page_ref_irq (page); -spin_lock (&mapping->tree_lock);if (PageDirty (page)) goto failed;BUG_ON (PagePrivate (page));__remove_from_page_cache (page); -spin_unlock (&mapping->tree_lock);unlock_page_ref_irq (page);ClearPageUptodate (page);page_cache_release (page);/* pagecache ref */ return 1 ; failed: -spin_unlock (&mapping->tree_lock);unlock_page_ref_irq (page);return 0 ; }
mm/vmscan.c +0 -4
@@ -391,7 +391,6 @@ int remove_mapping(struct address_spaceBUG_ON (mapping !=page_mapping (page));lock_page_ref_irq (page); -spin_lock (&mapping->tree_lock);/* * The non racy check for a busy page. *
@@ -426,13 +425,11 @@ int remove_mapping(struct address_spaceif (PageSwapCache (page)) {swp_entry_t swap = { .val =page_private (page) };__delete_from_swap_cache (page); -spin_unlock (&mapping->tree_lock);swap_free (swap); goto free_it; }__remove_from_page_cache (page); -spin_unlock (&mapping->tree_lock); free_it:unlock_page_ref_irq (page);
@@ -440,7 +437,6 @@ free_it:return 1 ; cannot_free: -spin_unlock (&mapping->tree_lock);unlock_page_ref_irq (page);return 0 ; }
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 2 lines · Peter Zijlstra
Based on Nick's lockless (read-side) pagecache patches (included in the series) here an attempt to make the write side concurrent.
On first glance it looks quite interesting and very innovative. Removing the tree_lock completely also reduces cache line usage. The page struct cacheline is already references in most contexts.
Quoted 1 line
Comment away ;-)
Could you post Nick's patches from your email addres and add a From Nick line in them? Its a bit confusing to have a patchset with different originating email addresses. Or does this come about by the evil header mangling of the list processor? Maybe you need to use >From ??
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 8 lines · Christoph Lameter
Quoted 2 lines · Peter Zijlstra
Based on Nick's lockless (read-side) pagecache patches (included in the series) here an attempt to make the write side concurrent.
On first glance it looks quite interesting and very innovative. Removing the
tree_lockcompletely also reduces cache line usage. The page struct cacheline is already references in most contexts.
Thanks, I'm just curious how bouncy the fine grained radix tree locks will be.
Quoted 6 lines
Quoted 1 line
Comment away ;-)
Could you post Nick's patches from your email addres and add a From Nick line in them? Its a bit confusing to have a patchset with different originating email addresses. Or does this come about by the evil header mangling of the list processor? Maybe you need to use >From ??
Nah that was on purpose, you can grab the patches from here:
http://programming.kicks-ass.net/kernel-patches/concurrent-pagecache-rt/
if you care, or I can post them again.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>