diff options
-rw-r--r-- | fs/buffer.c | 16 |
1 files changed, 15 insertions, 1 deletions
diff --git a/fs/buffer.c b/fs/buffer.c index 6f1ae3ac9789..2f8fcf7dd5c1 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -3250,7 +3250,7 @@ int try_to_free_buffers(struct page *page) { struct address_space * const mapping = page->mapping; struct buffer_head *buffers_to_free = NULL; - int ret = 0; + int ret = 0, extra; BUG_ON(!PageLocked(page)); if (PageWriteback(page)) @@ -3261,6 +3261,20 @@ int try_to_free_buffers(struct page *page) goto out; } + /* Page has a mapping and buffer so 2 extra references. */ + extra = 2; + /* If page is isolated from lru then it has an extra refcount. */ + extra += PageLRU(page) ? 0 : 1; + /* + * We do not want to free buffers attach to a page that is pinned by a + * GUP (get_user_pages()) as the driver/code path that did GUP might do + * a set_page_dirty() on the page when releasing the page. To test for + * GUP we check the refcount, if it is bigger than mapcount it means + * that the page is pinned by some GUP and we should keep the buffers. + */ + if ((page_count(page) - extra) > page_mapcount(page)) + return 0; + spin_lock(&mapping->private_lock); ret = drop_buffers(page, &buffers_to_free); |