aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>2013-02-11 15:40:45 +0900
committerDaniel Phillips <daniel@tux3.org>2013-02-11 15:40:45 +0900
commit9528403904fec1bbf9999fa910374170457d466b (patch)
tree0062c48c158cb9595d3036591302ef27fc6583bf
parent46de4a7f9511547dfb8d4b1f6506cdae9a9d6d2b (diff)
downloadlinux-tux3-9528403904fec1bbf9999fa910374170457d466b.tar.gz
tux3: Free pages related to inode for evict_inode() path
We call free_forked_buffers() on evict_inode() path to free forked buffers. Currently, free_forked_buffers() checks all pages even if inode was specified, to try to free forked pages as possible as early. But evict_inode() is called arbitrary timing, and pages on sb->forked_buffers list would usually be under I/O path. Because free_forked_buffers() is called for each end of commit (i.e. only if there is race with frontend and commit, pages are remaining on forked_buffers after commit). So, this changes to not try to free unrelated pages if inode was specified to free_forked_buffers(). Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
-rw-r--r--fs/tux3/buffer.h2
-rw-r--r--fs/tux3/buffer_fork.c19
-rw-r--r--fs/tux3/inode.c2
3 files changed, 12 insertions, 11 deletions
diff --git a/fs/tux3/buffer.h b/fs/tux3/buffer.h
index 0b3865661a8b4..e6c31a63fc3ce 100644
--- a/fs/tux3/buffer.h
+++ b/fs/tux3/buffer.h
@@ -151,7 +151,7 @@ static inline int buffer_forked(struct buffer_head *buffer)
return PageForked(buffer->b_page);
}
-void free_forked_buffers(struct sb *sb, struct inode *inode, int umount);
+void free_forked_buffers(struct sb *sb, struct inode *inode, int force);
struct buffer_head *blockdirty(struct buffer_head *buffer, unsigned newdelta);
struct page *pagefork_for_blockdirty(struct page *oldpage, unsigned newdelta);
int bufferfork_to_invalidate(struct address_space *mapping, struct page *page);
diff --git a/fs/tux3/buffer_fork.c b/fs/tux3/buffer_fork.c
index 9892ad77e7735..3cf1e25f278ec 100644
--- a/fs/tux3/buffer_fork.c
+++ b/fs/tux3/buffer_fork.c
@@ -113,17 +113,17 @@ static int is_freeable_forked(struct buffer_head *buffer, struct page *page)
}
/*
- * Try to free forked page. If it is called from umount or evict_inode
+ * Try to free forked page. (If it is called from umount or evict_inode
* path, there should be no referencer. So we free forked page
- * forcefully.
+ * forcefully.)
*
- * inode: If caller is evicting inode, pass inode. Otherwise NULL.
- * umount: If caller is umount path, 1. Otherwise 0.
+ * inode: Free only if page is related to this inode.
+ * force: If true, even if refcount != 0 try to free.
*
* FIXME: we need the better way, instead of polling the freeable
* forked pages periodically.
*/
-void free_forked_buffers(struct sb *sb, struct inode *inode, int umount)
+void free_forked_buffers(struct sb *sb, struct inode *inode, int force)
{
struct link free_list, *node, *prev, *n;
@@ -134,14 +134,15 @@ void free_forked_buffers(struct sb *sb, struct inode *inode, int umount)
link_for_each_safe(node, prev, n, &sb->forked_buffers) {
struct buffer_head *buffer = buffer_link_entry(node);
struct page *page = buffer->b_page;
- int force = 0;
trace_on("buffer %p, page %p, count %u",
buffer, page, page_count(page));
- /* We have to free this forked page forcefully */
- if (umount || (inode && inode->i_mapping == page->mapping))
- force = 1;
+ if (inode) {
+ /* Free only if page is related to inode */
+ if (page->mapping != inode->i_mapping)
+ continue;
+ }
#ifdef DISABLE_ASYNC_BACKEND
/* The page should already be submitted if no async frontend */
diff --git a/fs/tux3/inode.c b/fs/tux3/inode.c
index e6319ec95affd..b88be3a44f6f0 100644
--- a/fs/tux3/inode.c
+++ b/fs/tux3/inode.c
@@ -715,7 +715,7 @@ void tux3_evict_inode(struct inode *inode)
* refcount to free). If impossible, we would want to use
* per-inode forked-buffers list, instead.
*/
- free_forked_buffers(sb, inode, 0);
+ free_forked_buffers(sb, inode, 1);
clear_inode(inode);
free_xcache(inode);