Skip to content

Commit

Permalink
[PATCH] splice: fix page stealing LRU handling.
Browse files Browse the repository at this point in the history
Originally from Nick Piggin, just adapted to the newer branch.

You can't check PageLRU without holding zone->lru_lock.  The page
release code can get away with it only because the page refcount is 0 at
that point. Also, you can't reliably remove pages from the LRU unless
the refcount is 0. Ever.

Signed-off-by: Nick Piggin <nickpiggin@yahoo.com.au>
Signed-off-by: Jens Axboe <axboe@suse.de>
  • Loading branch information
Jens Axboe committed Apr 2, 2006
1 parent ad8d6f0 commit 3e7ee3e
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 19 deletions.
3 changes: 3 additions & 0 deletions fs/pipe.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ static void anon_pipe_buf_release(struct pipe_inode_info *info, struct pipe_buff
{
struct page *page = buf->page;

buf->flags &= ~PIPE_BUF_FLAG_STOLEN;

/*
* If nobody else uses this page, and we don't already have a
* temporary page, let's keep track of it as a one-deep
Expand Down Expand Up @@ -124,6 +126,7 @@ static void anon_pipe_buf_unmap(struct pipe_inode_info *info, struct pipe_buffer
static int anon_pipe_buf_steal(struct pipe_inode_info *info,
struct pipe_buffer *buf)
{
buf->flags |= PIPE_BUF_FLAG_STOLEN;
return 0;
}

Expand Down
30 changes: 11 additions & 19 deletions fs/splice.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *info,
if (!remove_mapping(mapping, page))
return 1;

if (PageLRU(page)) {
struct zone *zone = page_zone(page);

spin_lock_irq(&zone->lru_lock);
BUG_ON(!PageLRU(page));
__ClearPageLRU(page);
del_page_from_lru(zone, page);
spin_unlock_irq(&zone->lru_lock);
}

buf->flags |= PIPE_BUF_FLAG_STOLEN | PIPE_BUF_FLAG_LRU;
return 0;
}

Expand All @@ -85,6 +76,7 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *info,
{
page_cache_release(buf->page);
buf->page = NULL;
buf->flags &= ~(PIPE_BUF_FLAG_STOLEN | PIPE_BUF_FLAG_LRU);
}

static void *page_cache_pipe_buf_map(struct file *file,
Expand Down Expand Up @@ -414,11 +406,12 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
{
struct file *file = sd->file;
struct address_space *mapping = file->f_mapping;
gfp_t gfp_mask = mapping_gfp_mask(mapping);
unsigned int offset;
struct page *page;
pgoff_t index;
char *src;
int ret, stolen;
int ret;

/*
* after this, page will be locked and unmapped
Expand All @@ -429,7 +422,6 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,

index = sd->pos >> PAGE_CACHE_SHIFT;
offset = sd->pos & ~PAGE_CACHE_MASK;
stolen = 0;

/*
* reuse buf page, if SPLICE_F_MOVE is set
Expand All @@ -443,15 +435,15 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
goto find_page;

page = buf->page;
stolen = 1;
if (add_to_page_cache_lru(page, mapping, index,
mapping_gfp_mask(mapping)))
if (add_to_page_cache(page, mapping, index, gfp_mask))
goto find_page;

if (!(buf->flags & PIPE_BUF_FLAG_LRU))
lru_cache_add(page);
} else {
find_page:
ret = -ENOMEM;
page = find_or_create_page(mapping, index,
mapping_gfp_mask(mapping));
page = find_or_create_page(mapping, index, gfp_mask);
if (!page)
goto out;

Expand Down Expand Up @@ -494,7 +486,7 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
} else if (ret)
goto out;

if (!stolen) {
if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) {
char *dst = kmap_atomic(page, KM_USER0);

memcpy(dst + offset, src + buf->offset, sd->len);
Expand All @@ -511,7 +503,7 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,

balance_dirty_pages_ratelimited(mapping);
out:
if (!stolen) {
if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) {
page_cache_release(page);
unlock_page(page);
}
Expand Down
4 changes: 4 additions & 0 deletions include/linux/pipe_fs_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@

#define PIPE_BUFFERS (16)

#define PIPE_BUF_FLAG_STOLEN 0x01
#define PIPE_BUF_FLAG_LRU 0x02

struct pipe_buffer {
struct page *page;
unsigned int offset, len;
struct pipe_buf_operations *ops;
unsigned int flags;
};

struct pipe_buf_operations {
Expand Down

0 comments on commit 3e7ee3e

Please sign in to comment.