diff options
author | Linus Torvalds <torvalds@evo.osdl.org> | 2005-01-08 04:26:48 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-01-08 04:26:48 -0800 |
commit | 530f890b9bb2543189c2a9b97bc2b8d44ed2788c (patch) | |
tree | 6531f9611a48d7a49b2a9dc1f8a79f2fa8bd0042 /fs | |
parent | 0f1d4813a4a65296e1131f320a60741732bc068f (diff) | |
download | history-530f890b9bb2543189c2a9b97bc2b8d44ed2788c.tar.gz |
Re-use the buffer page for pipe data.
This brings the latency back to previous levels, and in
fact seems to drive it a bit lower.
Diffstat (limited to 'fs')
-rw-r--r-- | fs/pipe.c | 43 |
1 files changed, 26 insertions, 17 deletions
diff --git a/fs/pipe.c b/fs/pipe.c index eb7df855732ec5..e98b1cb82705e9 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -86,6 +86,17 @@ pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len) return 0; } +static void release_pipe_buf(struct pipe_inode_info *info, struct pipe_buffer *buf) +{ + struct page *page = buf->page; + + if (info->tmp_page) { + __free_page(page); + return; + } + info->tmp_page = page; +} + static ssize_t pipe_readv(struct file *filp, const struct iovec *_iov, unsigned long nr_segs, loff_t *ppos) @@ -127,7 +138,7 @@ pipe_readv(struct file *filp, const struct iovec *_iov, buf->offset += chars; buf->len -= chars; if (!buf->len) { - __free_page(buf->page); + release_pipe_buf(info, buf); buf->page = NULL; curbuf = (curbuf + 1) & (PIPE_BUFFERS-1); info->curbuf = curbuf; @@ -236,12 +247,16 @@ pipe_writev(struct file *filp, const struct iovec *_iov, ssize_t chars; int newbuf = (info->curbuf + bufs) & (PIPE_BUFFERS-1); struct pipe_buffer *buf = info->bufs + newbuf; - struct page *page = alloc_page(GFP_USER); + struct page *page = info->tmp_page; int error; - if (unlikely(!page)) { - ret = ret ? : -ENOMEM; - break; + if (!page) { + page = alloc_page(GFP_KERNEL); + if (unlikely(!page)) { + ret = ret ? : -ENOMEM; + break; + } + info->tmp_page = page; } /* Always wakeup, even if the copy fails. Otherwise * we lock up (O_NONBLOCK-)readers that sleep due to @@ -257,7 +272,6 @@ pipe_writev(struct file *filp, const struct iovec *_iov, kunmap(page); if (unlikely(error)) { if (!ret) ret = -EFAULT; - __free_page(page); break; } ret += chars; @@ -267,6 +281,7 @@ pipe_writev(struct file *filp, const struct iovec *_iov, buf->offset = 0; buf->len = chars; info->nrbufs = ++bufs; + info->tmp_page = NULL; total_len -= chars; if (!total_len) @@ -593,25 +608,20 @@ void free_pipe_info(struct inode *inode) struct pipe_inode_info *info = inode->i_pipe; inode->i_pipe = NULL; + if (info->tmp_page) + __free_page(info->tmp_page); for (i = 0; i < PIPE_BUFFERS; i++) { - struct page *page = info->bufs[i].page; - - /* We'll make this a data-dependent free some day .. */ - if (page) - __free_page(page); + struct pipe_buffer *buf = info->bufs + i; + if (buf->page) + release_pipe_buf(info, buf); } kfree(info); } struct inode* pipe_new(struct inode* inode) { - unsigned long page; struct pipe_inode_info *info; - page = __get_free_page(GFP_USER); - if (!page) - return NULL; - info = kmalloc(sizeof(struct pipe_inode_info), GFP_KERNEL); if (!info) goto fail_page; @@ -623,7 +633,6 @@ struct inode* pipe_new(struct inode* inode) return inode; fail_page: - free_page(page); return NULL; } |