Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 217798
b: refs/heads/master
c: 9e38d86
h: refs/heads/master
v: v3
  • Loading branch information
Nick Piggin authored and Al Viro committed Oct 26, 2010
1 parent 4388fc4 commit 06195a6
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 42 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: cffbc8aa334f55c9ed42d25202eb3ebf3a97c195
refs/heads/master: 9e38d86ff2d8a8db99570e982230861046df32b5
11 changes: 4 additions & 7 deletions trunk/fs/fs-writeback.c
Original file line number Diff line number Diff line change
Expand Up @@ -408,16 +408,13 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
* completion.
*/
redirty_tail(inode);
} else if (atomic_read(&inode->i_count)) {
/*
* The inode is clean, inuse
*/
list_move(&inode->i_list, &inode_in_use);
} else {
/*
* The inode is clean, unused
* The inode is clean. At this point we either have
* a reference to the inode or it's on it's way out.
* No need to add it back to the LRU.
*/
list_move(&inode->i_list, &inode_unused);
list_del_init(&inode->i_list);
}
}
inode_sync_complete(inode);
Expand Down
86 changes: 60 additions & 26 deletions trunk/fs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ static unsigned int i_hash_shift __read_mostly;
* allowing for low-overhead inode sync() operations.
*/

LIST_HEAD(inode_in_use);
LIST_HEAD(inode_unused);
static LIST_HEAD(inode_unused);
static struct hlist_head *inode_hashtable __read_mostly;

/*
Expand Down Expand Up @@ -291,6 +290,7 @@ void inode_init_once(struct inode *inode)
INIT_HLIST_NODE(&inode->i_hash);
INIT_LIST_HEAD(&inode->i_dentry);
INIT_LIST_HEAD(&inode->i_devices);
INIT_LIST_HEAD(&inode->i_list);
INIT_RADIX_TREE(&inode->i_data.page_tree, GFP_ATOMIC);
spin_lock_init(&inode->i_data.tree_lock);
spin_lock_init(&inode->i_data.i_mmap_lock);
Expand All @@ -317,12 +317,23 @@ static void init_once(void *foo)
*/
void __iget(struct inode *inode)
{
if (atomic_inc_return(&inode->i_count) != 1)
return;
atomic_inc(&inode->i_count);
}

if (!(inode->i_state & (I_DIRTY|I_SYNC)))
list_move(&inode->i_list, &inode_in_use);
percpu_counter_dec(&nr_inodes_unused);
static void inode_lru_list_add(struct inode *inode)
{
if (list_empty(&inode->i_list)) {
list_add(&inode->i_list, &inode_unused);
percpu_counter_inc(&nr_inodes_unused);
}
}

static void inode_lru_list_del(struct inode *inode)
{
if (!list_empty(&inode->i_list)) {
list_del_init(&inode->i_list);
percpu_counter_dec(&nr_inodes_unused);
}
}

void end_writeback(struct inode *inode)
Expand Down Expand Up @@ -367,7 +378,7 @@ static void dispose_list(struct list_head *head)
struct inode *inode;

inode = list_first_entry(head, struct inode, i_list);
list_del(&inode->i_list);
list_del_init(&inode->i_list);

evict(inode);

Expand Down Expand Up @@ -413,7 +424,8 @@ static int invalidate_list(struct list_head *head, struct list_head *dispose)
list_move(&inode->i_list, dispose);
WARN_ON(inode->i_state & I_NEW);
inode->i_state |= I_FREEING;
percpu_counter_dec(&nr_inodes_unused);
if (!(inode->i_state & (I_DIRTY | I_SYNC)))
percpu_counter_dec(&nr_inodes_unused);
continue;
}
busy = 1;
Expand Down Expand Up @@ -448,7 +460,7 @@ int invalidate_inodes(struct super_block *sb)

static int can_unuse(struct inode *inode)
{
if (inode->i_state)
if (inode->i_state & ~I_REFERENCED)
return 0;
if (inode_has_buffers(inode))
return 0;
Expand All @@ -460,17 +472,20 @@ static int can_unuse(struct inode *inode)
}

/*
* Scan `goal' inodes on the unused list for freeable ones. They are moved to
* a temporary list and then are freed outside inode_lock by dispose_list().
* Scan `goal' inodes on the unused list for freeable ones. They are moved to a
* temporary list and then are freed outside inode_lock by dispose_list().
*
* Any inodes which are pinned purely because of attached pagecache have their
* pagecache removed. We expect the final iput() on that inode to add it to
* the front of the inode_unused list. So look for it there and if the
* inode is still freeable, proceed. The right inode is found 99.9% of the
* time in testing on a 4-way.
* pagecache removed. If the inode has metadata buffers attached to
* mapping->private_list then try to remove them.
*
* If the inode has metadata buffers attached to mapping->private_list then
* try to remove them.
* If the inode has the I_REFERENCED flag set, then it means that it has been
* used recently - the flag is set in iput_final(). When we encounter such an
* inode, clear the flag and move it to the back of the LRU so it gets another
* pass through the LRU before it gets reclaimed. This is necessary because of
* the fact we are doing lazy LRU updates to minimise lock contention so the
* LRU does not have strict ordering. Hence we don't want to reclaim inodes
* with this flag set because they are the inodes that are out of order.
*/
static void prune_icache(int nr_to_scan)
{
Expand All @@ -488,8 +503,21 @@ static void prune_icache(int nr_to_scan)

inode = list_entry(inode_unused.prev, struct inode, i_list);

if (inode->i_state || atomic_read(&inode->i_count)) {
/*
* Referenced or dirty inodes are still in use. Give them
* another pass through the LRU as we canot reclaim them now.
*/
if (atomic_read(&inode->i_count) ||
(inode->i_state & ~I_REFERENCED)) {
list_del_init(&inode->i_list);
percpu_counter_dec(&nr_inodes_unused);
continue;
}

/* recently referenced inodes get one more pass */
if (inode->i_state & I_REFERENCED) {
list_move(&inode->i_list, &inode_unused);
inode->i_state &= ~I_REFERENCED;
continue;
}
if (inode_has_buffers(inode) || inode->i_data.nrpages) {
Expand Down Expand Up @@ -620,7 +648,6 @@ static inline void
__inode_add_to_lists(struct super_block *sb, struct hlist_head *head,
struct inode *inode)
{
list_add(&inode->i_list, &inode_in_use);
list_add(&inode->i_sb_list, &sb->s_inodes);
if (head)
hlist_add_head(&inode->i_hash, head);
Expand Down Expand Up @@ -1237,10 +1264,11 @@ static void iput_final(struct inode *inode)
drop = generic_drop_inode(inode);

if (!drop) {
if (!(inode->i_state & (I_DIRTY|I_SYNC)))
list_move(&inode->i_list, &inode_unused);
percpu_counter_inc(&nr_inodes_unused);
if (sb->s_flags & MS_ACTIVE) {
inode->i_state |= I_REFERENCED;
if (!(inode->i_state & (I_DIRTY|I_SYNC))) {
inode_lru_list_add(inode);
}
spin_unlock(&inode_lock);
return;
}
Expand All @@ -1251,13 +1279,19 @@ static void iput_final(struct inode *inode)
spin_lock(&inode_lock);
WARN_ON(inode->i_state & I_NEW);
inode->i_state &= ~I_WILL_FREE;
percpu_counter_dec(&nr_inodes_unused);
hlist_del_init(&inode->i_hash);
}
list_del_init(&inode->i_list);
list_del_init(&inode->i_sb_list);
WARN_ON(inode->i_state & I_NEW);
inode->i_state |= I_FREEING;

/*
* After we delete the inode from the LRU here, we avoid moving dirty
* inodes back onto the LRU now because I_FREEING is set and hence
* writeback_single_inode() won't move the inode around.
*/
inode_lru_list_del(inode);

list_del_init(&inode->i_sb_list);
spin_unlock(&inode_lock);
evict(inode);
spin_lock(&inode_lock);
Expand Down
13 changes: 7 additions & 6 deletions trunk/include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1641,16 +1641,17 @@ struct super_operations {
*
* Q: What is the difference between I_WILL_FREE and I_FREEING?
*/
#define I_DIRTY_SYNC 1
#define I_DIRTY_DATASYNC 2
#define I_DIRTY_PAGES 4
#define I_DIRTY_SYNC (1 << 0)
#define I_DIRTY_DATASYNC (1 << 1)
#define I_DIRTY_PAGES (1 << 2)
#define __I_NEW 3
#define I_NEW (1 << __I_NEW)
#define I_WILL_FREE 16
#define I_FREEING 32
#define I_CLEAR 64
#define I_WILL_FREE (1 << 4)
#define I_FREEING (1 << 5)
#define I_CLEAR (1 << 6)
#define __I_SYNC 7
#define I_SYNC (1 << __I_SYNC)
#define I_REFERENCED (1 << 8)

#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)

Expand Down
2 changes: 0 additions & 2 deletions trunk/include/linux/writeback.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
struct backing_dev_info;

extern spinlock_t inode_lock;
extern struct list_head inode_in_use;
extern struct list_head inode_unused;

/*
* fs/fs-writeback.c
Expand Down

0 comments on commit 06195a6

Please sign in to comment.