From: Paul Jackson Date: Tue, 23 May 2006 00:56:07 +0000 (-0700) Subject: [PATCH] Cpuset: might sleep checking zones allowed fix X-Git-Tag: v2.6.16.28-rc1~56 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=e81ccf5afaf9cff1ed27c29ff96795c146a3d571;p=karo-tx-linux.git [PATCH] Cpuset: might sleep checking zones allowed fix Fix an infrequently encountered 'sleeping function called from invalid context' in the cpuset hooks in __alloc_pages. Could sleep while interrupts disabled. The routine cpuset_zone_allowed() is called by code in mm/page_alloc.c __alloc_pages() to determine if a zone is allowed in the current tasks cpuset. This routine can sleep, for certain GFP_KERNEL allocations, if the zone is on a memory node not allowed in the current cpuset, but might be allowed in a parent cpuset. But we can't sleep in __alloc_pages() if in interrupt, nor if called for a GFP_ATOMIC request (__GFP_WAIT not set in gfp_flags). The rule was intended to be: Don't call cpuset_zone_allowed() if you can't sleep, unless you pass in the __GFP_HARDWALL flag set in gfp_flag, which disables the code that might scan up ancestor cpusets and sleep. This rule was being violated due to a bogus change made (by myself, pj) to __alloc_pages() as part of the November 2005 effort to cleanup its logic. The bogus change can be seen at: http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-11/4691.html [PATCH 01/05] mm fix __alloc_pages cpuset ALLOC_* flags This was first noticed on a tight memory system, in code that was disabling interrupts and doing allocation requests with __GFP_WAIT not set, which resulted in __might_sleep() writing complaints to the log "Debug: sleeping function called ...", when the code in cpuset_zone_allowed() tried to take the callback_sem cpuset semaphore. Special thanks to Dave Chinner, for figuring this out, and a tip of the hat to Nick Piggin who warned me of this back in Nov 2005, before I was ready to listen. Signed-off-by: Paul Jackson Signed-off-by: Chris Wright Signed-off-by: Greg Kroah-Hartman --- diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 61de2220231e..8b3cde1eb45e 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -949,7 +949,8 @@ restart: alloc_flags |= ALLOC_HARDER; if (gfp_mask & __GFP_HIGH) alloc_flags |= ALLOC_HIGH; - alloc_flags |= ALLOC_CPUSET; + if (wait) + alloc_flags |= ALLOC_CPUSET; /* * Go through the zonelist again. Let __GFP_HIGH and allocations