aboutsummaryrefslogtreecommitdiffstats
path: root/mm/slab.c
diff options
context:
space:
mode:
authorMichal Hocko <mhocko@suse.com>2015-08-21 17:11:51 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-08-21 17:30:10 -0400
commit2f064f3485cd29633ad1b3cfb00cc519509a3d72 (patch)
tree1581de52d696908aea01910e2a25b6725f4ccaa2 /mm/slab.c
parente45fc85a2f371f388ff3804271375a1aedbe3744 (diff)
mm: make page pfmemalloc check more robust
Commit c48a11c7ad26 ("netvm: propagate page->pfmemalloc to skb") added checks for page->pfmemalloc to __skb_fill_page_desc(): if (page->pfmemalloc && !page->mapping) skb->pfmemalloc = true; It assumes page->mapping == NULL implies that page->pfmemalloc can be trusted. However, __delete_from_page_cache() can set set page->mapping to NULL and leave page->index value alone. Due to being in union, a non-zero page->index will be interpreted as true page->pfmemalloc. So the assumption is invalid if the networking code can see such a page. And it seems it can. We have encountered this with a NFS over loopback setup when such a page is attached to a new skbuf. There is no copying going on in this case so the page confuses __skb_fill_page_desc which interprets the index as pfmemalloc flag and the network stack drops packets that have been allocated using the reserves unless they are to be queued on sockets handling the swapping which is the case here and that leads to hangs when the nfs client waits for a response from the server which has been dropped and thus never arrive. The struct page is already heavily packed so rather than finding another hole to put it in, let's do a trick instead. We can reuse the index again but define it to an impossible value (-1UL). This is the page index so it should never see the value that large. Replace all direct users of page->pfmemalloc by page_is_pfmemalloc which will hide this nastiness from unspoiled eyes. The information will get lost if somebody wants to use page->index obviously but that was the case before and the original code expected that the information should be persisted somewhere else if that is really needed (e.g. what SLAB and SLUB do). [akpm@linux-foundation.org: fix blooper in slub] Fixes: c48a11c7ad26 ("netvm: propagate page->pfmemalloc to skb") Signed-off-by: Michal Hocko <mhocko@suse.com> Debugged-by: Vlastimil Babka <vbabka@suse.com> Debugged-by: Jiri Bohac <jbohac@suse.com> Cc: Eric Dumazet <eric.dumazet@gmail.com> Cc: David Miller <davem@davemloft.net> Acked-by: Mel Gorman <mgorman@suse.de> Cc: <stable@vger.kernel.org> [3.6+] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/slab.c')
-rw-r--r--mm/slab.c4
1 files changed, 2 insertions, 2 deletions
diff --git a/mm/slab.c b/mm/slab.c
index 200e22412a16..bbd0b47dc6a9 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -1603,7 +1603,7 @@ static struct page *kmem_getpages(struct kmem_cache *cachep, gfp_t flags,
1603 } 1603 }
1604 1604
1605 /* Record if ALLOC_NO_WATERMARKS was set when allocating the slab */ 1605 /* Record if ALLOC_NO_WATERMARKS was set when allocating the slab */
1606 if (unlikely(page->pfmemalloc)) 1606 if (page_is_pfmemalloc(page))
1607 pfmemalloc_active = true; 1607 pfmemalloc_active = true;
1608 1608
1609 nr_pages = (1 << cachep->gfporder); 1609 nr_pages = (1 << cachep->gfporder);
@@ -1614,7 +1614,7 @@ static struct page *kmem_getpages(struct kmem_cache *cachep, gfp_t flags,
1614 add_zone_page_state(page_zone(page), 1614 add_zone_page_state(page_zone(page),
1615 NR_SLAB_UNRECLAIMABLE, nr_pages); 1615 NR_SLAB_UNRECLAIMABLE, nr_pages);
1616 __SetPageSlab(page); 1616 __SetPageSlab(page);
1617 if (page->pfmemalloc) 1617 if (page_is_pfmemalloc(page))
1618 SetPageSlabPfmemalloc(page); 1618 SetPageSlabPfmemalloc(page);
1619 1619
1620 if (kmemcheck_enabled && !(cachep->flags & SLAB_NOTRACK)) { 1620 if (kmemcheck_enabled && !(cachep->flags & SLAB_NOTRACK)) {