mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-06-17 20:25:19 +00:00
virtio_balloon: fix deadlock on OOM
fill_balloon doing memory allocations under balloon_lock can cause a deadlock when leak_balloon is called from virtballoon_oom_notify and tries to take same lock. To fix, split page allocation and enqueue and do allocations outside the lock. Here's a detailed analysis of the deadlock by Tetsuo Handa: In leak_balloon(), mutex_lock(&vb->balloon_lock) is called in order to serialize against fill_balloon(). But in fill_balloon(), alloc_page(GFP_HIGHUSER[_MOVABLE] | __GFP_NOMEMALLOC | __GFP_NORETRY) is called with vb->balloon_lock mutex held. Since GFP_HIGHUSER[_MOVABLE] implies __GFP_DIRECT_RECLAIM | __GFP_IO | __GFP_FS, despite __GFP_NORETRY is specified, this allocation attempt might indirectly depend on somebody else's __GFP_DIRECT_RECLAIM memory allocation. And such indirect __GFP_DIRECT_RECLAIM memory allocation might call leak_balloon() via virtballoon_oom_notify() via blocking_notifier_call_chain() callback via out_of_memory() when it reached __alloc_pages_may_oom() and held oom_lock mutex. Since vb->balloon_lock mutex is already held by fill_balloon(), it will cause OOM lockup. Thread1 Thread2 fill_balloon() takes a balloon_lock balloon_page_enqueue() alloc_page(GFP_HIGHUSER_MOVABLE) direct reclaim (__GFP_FS context) takes a fs lock waits for that fs lock alloc_page(GFP_NOFS) __alloc_pages_may_oom() takes the oom_lock out_of_memory() blocking_notifier_call_chain() leak_balloon() tries to take that balloon_lock and deadlocks Reported-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Cc: Michal Hocko <mhocko@suse.com> Cc: Wei Wang <wei.w.wang@intel.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
bebc6082da
commit
c7cdff0e86
3 changed files with 74 additions and 13 deletions
|
@ -50,6 +50,7 @@
|
|||
#include <linux/gfp.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
/*
|
||||
* Balloon device information descriptor.
|
||||
|
@ -67,7 +68,9 @@ struct balloon_dev_info {
|
|||
struct inode *inode;
|
||||
};
|
||||
|
||||
extern struct page *balloon_page_enqueue(struct balloon_dev_info *b_dev_info);
|
||||
extern struct page *balloon_page_alloc(void);
|
||||
extern void balloon_page_enqueue(struct balloon_dev_info *b_dev_info,
|
||||
struct page *page);
|
||||
extern struct page *balloon_page_dequeue(struct balloon_dev_info *b_dev_info);
|
||||
|
||||
static inline void balloon_devinfo_init(struct balloon_dev_info *balloon)
|
||||
|
@ -193,4 +196,34 @@ static inline gfp_t balloon_mapping_gfp_mask(void)
|
|||
}
|
||||
|
||||
#endif /* CONFIG_BALLOON_COMPACTION */
|
||||
|
||||
/*
|
||||
* balloon_page_push - insert a page into a page list.
|
||||
* @head : pointer to list
|
||||
* @page : page to be added
|
||||
*
|
||||
* Caller must ensure the page is private and protect the list.
|
||||
*/
|
||||
static inline void balloon_page_push(struct list_head *pages, struct page *page)
|
||||
{
|
||||
list_add(&page->lru, pages);
|
||||
}
|
||||
|
||||
/*
|
||||
* balloon_page_pop - remove a page from a page list.
|
||||
* @head : pointer to list
|
||||
* @page : page to be added
|
||||
*
|
||||
* Caller must ensure the page is private and protect the list.
|
||||
*/
|
||||
static inline struct page *balloon_page_pop(struct list_head *pages)
|
||||
{
|
||||
struct page *page = list_first_entry_or_null(pages, struct page, lru);
|
||||
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
list_del(&page->lru);
|
||||
return page;
|
||||
}
|
||||
#endif /* _LINUX_BALLOON_COMPACTION_H */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue