Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 41 additions & 44 deletions block/bdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -1336,56 +1336,53 @@ void bdev_mark_dead(struct block_device *bdev, bool surprise)
*/
EXPORT_SYMBOL_GPL(bdev_mark_dead);

void sync_bdevs(bool wait)
static int sync_bdevs_inode_iter_cb(struct inode *inode, void *data)
{
struct inode *inode, *old_inode = NULL;

spin_lock(&blockdev_superblock->s_inode_list_lock);
list_for_each_entry(inode, &blockdev_superblock->s_inodes, i_sb_list) {
struct address_space *mapping = inode->i_mapping;
struct block_device *bdev;
bool wait = (bool)data;
struct block_device *bdev;
struct address_space *mapping = inode->i_mapping;

spin_lock(&inode->i_lock);
if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW) ||
mapping->nrpages == 0) {
spin_unlock(&inode->i_lock);
continue;
}
__iget(inode);
if (mapping->nrpages == 0) {
spin_unlock(&inode->i_lock);
spin_unlock(&blockdev_superblock->s_inode_list_lock);
return 0;
}

/*
* We hold a reference to 'inode' so it couldn't have been
* removed from s_inodes list while we dropped the
* s_inode_list_lock.
*/
__iget(inode);
spin_unlock(&inode->i_lock);
spin_unlock(&blockdev_superblock->s_inode_list_lock);
bdev = I_BDEV(inode);

mutex_lock(&bdev->bd_disk->open_mutex);
if (!atomic_read(&bdev->bd_openers)) {
; /* skip */
} else if (wait) {
/*
* We hold a reference to 'inode' so it couldn't have been
* removed from s_inodes list while we dropped the
* s_inode_list_lock We cannot iput the inode now as we can
* be holding the last reference and we cannot iput it under
* s_inode_list_lock. So we keep the reference and iput it
* later.
* We keep the error status of individual mapping so
* that applications can catch the writeback error using
* fsync(2). See filemap_fdatawait_keep_errors() for
* details.
*/
iput(old_inode);
old_inode = inode;
bdev = I_BDEV(inode);

mutex_lock(&bdev->bd_disk->open_mutex);
if (!atomic_read(&bdev->bd_openers)) {
; /* skip */
} else if (wait) {
/*
* We keep the error status of individual mapping so
* that applications can catch the writeback error using
* fsync(2). See filemap_fdatawait_keep_errors() for
* details.
*/
filemap_fdatawait_keep_errors(inode->i_mapping);
} else {
filemap_fdatawrite(inode->i_mapping);
}
mutex_unlock(&bdev->bd_disk->open_mutex);

spin_lock(&blockdev_superblock->s_inode_list_lock);
filemap_fdatawait_keep_errors(inode->i_mapping);
} else {
filemap_fdatawrite(inode->i_mapping);
}
spin_unlock(&blockdev_superblock->s_inode_list_lock);
iput(old_inode);
mutex_unlock(&bdev->bd_disk->open_mutex);
iput(inode);

spin_lock(&blockdev_superblock->s_inode_list_lock);

return 0;
}

void sync_bdevs(bool wait)
{
sb_for_each_inodes(blockdev_superblock, INODE_ITER_NORMAL,
sync_bdevs_inode_iter_cb, (void *)wait);
}

/*
Expand Down
44 changes: 19 additions & 25 deletions fs/drop_caches.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,30 @@
/* A global variable is a bit ugly, but it keeps the code simple */
static int sysctl_drop_caches;

static void drop_pagecache_sb(struct super_block *sb, void *unused)
static int drop_pagecache_inode_iter_cb(struct inode *inode, void *unused)
{
struct inode *inode, *toput_inode = NULL;
struct super_block *sb = inode->i_sb;

spin_lock(&sb->s_inode_list_lock);
list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
spin_lock(&inode->i_lock);
/*
* We must skip inodes in unusual state. We may also skip
* inodes without pages but we deliberately won't in case
* we need to reschedule to avoid softlockups.
*/
if ((inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) ||
(mapping_empty(inode->i_mapping) && !need_resched())) {
spin_unlock(&inode->i_lock);
continue;
}
__iget(inode);
if (mapping_empty(inode->i_mapping)) {
spin_unlock(&inode->i_lock);
spin_unlock(&sb->s_inode_list_lock);

invalidate_mapping_pages(inode->i_mapping, 0, -1);
iput(toput_inode);
toput_inode = inode;

cond_resched();
spin_lock(&sb->s_inode_list_lock);
return 0;
}

__iget(inode);
spin_unlock(&inode->i_lock);
spin_unlock(&sb->s_inode_list_lock);
iput(toput_inode);

invalidate_mapping_pages(inode->i_mapping, 0, -1);
iput(inode);

spin_lock(&sb->s_inode_list_lock);

return 0;
}

static void drop_pagecache_sb(struct super_block *sb, void *unused)
{
sb_for_each_inodes(sb, INODE_ITER_NORMAL, drop_pagecache_inode_iter_cb, NULL);
}

static int drop_caches_sysctl_handler(const struct ctl_table *table, int write,
Expand Down
38 changes: 15 additions & 23 deletions fs/gfs2/ops_fstype.c
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ static int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent)
switch (token) {
case Opt_jid:
ret = match_int(&tmp[0], &option);
if (ret || option < 0)
if (ret || option < 0)
goto hostdata_error;
if (test_and_clear_bit(SDF_NOJOURNALID, &sdp->sd_flags))
ls->ls_jid = option;
Expand Down Expand Up @@ -1719,6 +1719,19 @@ static int gfs2_meta_init_fs_context(struct fs_context *fc)
return 0;
}

static int gfs2_evict_inode_iter_cb(struct inode *inode, void *unused)
{
struct super_block *sb = inode->i_sb;

__iget(inode);
spin_unlock(&inode->i_lock);
spin_unlock(&sb->s_inode_list_lock);

iput(inode);
spin_lock(&sb->s_inode_list_lock);
return 0;
}

/**
* gfs2_evict_inodes - evict inodes cooperatively
* @sb: the superblock
Expand All @@ -1741,31 +1754,10 @@ static int gfs2_meta_init_fs_context(struct fs_context *fc)
*/
static void gfs2_evict_inodes(struct super_block *sb)
{
struct inode *inode, *toput_inode = NULL;
struct gfs2_sbd *sdp = sb->s_fs_info;

set_bit(SDF_EVICTING, &sdp->sd_flags);

spin_lock(&sb->s_inode_list_lock);
list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
spin_lock(&inode->i_lock);
if ((inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) &&
!need_resched()) {
spin_unlock(&inode->i_lock);
continue;
}
__iget(inode);
spin_unlock(&inode->i_lock);
spin_unlock(&sb->s_inode_list_lock);

iput(toput_inode);
toput_inode = inode;

cond_resched();
spin_lock(&sb->s_inode_list_lock);
}
spin_unlock(&sb->s_inode_list_lock);
iput(toput_inode);
sb_for_each_inodes(sb, INODE_ITER_NORMAL, gfs2_evict_inode_iter_cb, NULL);
}

static void gfs2_kill_sb(struct super_block *sb)
Expand Down
142 changes: 106 additions & 36 deletions fs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ const struct address_space_operations empty_aops = {
};
EXPORT_SYMBOL(empty_aops);

struct inode_iter {
struct list_head iters_node; /* sb->s_inodes_iters */
struct list_head *next; /* next node going to iterate */
unsigned int flags;
inode_iter_cb func;
void *data;
int ret;
};

static DEFINE_PER_CPU(unsigned long, nr_inodes);
static DEFINE_PER_CPU(unsigned long, nr_unused);

Expand Down Expand Up @@ -641,12 +650,96 @@ void inode_sb_list_add(struct inode *inode)
}
EXPORT_SYMBOL_GPL(inode_sb_list_add);

static void inode_sb_iter_start(struct super_block *sb, struct inode_iter *it,
unsigned int flags, inode_iter_cb fn, void *data)
{
it->flags = flags;
it->func = fn;
it->data = data;
it->ret = 0;
spin_lock(&sb->s_inode_list_lock);
it->next = sb->s_inodes.next;
list_add(&it->iters_node, &sb->s_inodes_iters);
}

static void inode_sb_iter_end(struct inode_iter *it, struct super_block *sb)
{
list_del(&it->iters_node);
spin_unlock(&sb->s_inode_list_lock);
}

static bool inode_sb_iter_next(struct inode_iter *it, struct super_block *sb)
{
struct inode *inode = NULL;
int ret;

while (!inode && it->next != &sb->s_inodes) {
inode = list_entry(it->next, struct inode, i_sb_list);
if (it->flags & INODE_ITER_UNUSED) {
if (icount_read_once(inode)) {
it->next = it->next->next;
continue;
}

spin_lock(&inode->i_lock);
if (icount_read(inode)) {
spin_unlock(&inode->i_lock);
it->next = it->next->next;
continue;
}
} else {
spin_lock(&inode->i_lock);
}

if ((it->flags & INODE_ITER_NORMAL) &&
(inode_state_read(inode) & (I_NEW | I_FREEING | I_WILL_FREE))) {
spin_unlock(&inode->i_lock);
it->next = it->next->next;
continue;
}

it->next = it->next->next;
ret = it->func(inode, it->data);
if (ret) {
it->ret = ret;
return false;
}

if (need_resched()) {
spin_unlock(&sb->s_inode_list_lock);
cond_resched();
spin_lock(&sb->s_inode_list_lock);
}
}

return it->next == &sb->s_inodes ? false : true;
}

int sb_for_each_inodes(struct super_block *sb, unsigned int flags,
inode_iter_cb fn, void *data)
{
struct inode_iter it;

inode_sb_iter_start(sb, &it, flags, fn, data);
while (inode_sb_iter_next(&it, sb))
;
inode_sb_iter_end(&it, sb);

return it.ret;
}
EXPORT_SYMBOL(sb_for_each_inodes);

static inline void inode_sb_list_del(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
struct inode_iter *it;

if (!list_empty(&inode->i_sb_list)) {
spin_lock(&sb->s_inode_list_lock);
list_for_each_entry(it, &sb->s_inodes_iters, iters_node) {
if (it->next == &inode->i_sb_list)
it->next = inode->i_sb_list.next;
}
list_del_init(&inode->i_sb_list);
spin_unlock(&sb->s_inode_list_lock);
}
Expand Down Expand Up @@ -866,6 +959,17 @@ static void dispose_list(struct list_head *head)
}
}

static int evict_inodes_inode_iter_cb(struct inode *inode, void *data)
{
struct list_head *dispose = (struct list_head *)data;

inode_state_set(inode, I_FREEING);
inode_lru_list_del(inode);
spin_unlock(&inode->i_lock);
list_add(&inode->i_lru, dispose);
return 0;
}

/**
* evict_inodes - evict all evictable inodes for a superblock
* @sb: superblock to operate on
Expand All @@ -877,44 +981,10 @@ static void dispose_list(struct list_head *head)
*/
void evict_inodes(struct super_block *sb)
{
struct inode *inode;
LIST_HEAD(dispose);
unsigned int flags = INODE_ITER_NORMAL | INODE_ITER_UNUSED;

again:
spin_lock(&sb->s_inode_list_lock);
list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
if (icount_read_once(inode))
continue;

spin_lock(&inode->i_lock);
if (icount_read(inode)) {
spin_unlock(&inode->i_lock);
continue;
}
if (inode_state_read(inode) & (I_NEW | I_FREEING | I_WILL_FREE)) {
spin_unlock(&inode->i_lock);
continue;
}

inode_state_set(inode, I_FREEING);
inode_lru_list_del(inode);
spin_unlock(&inode->i_lock);
list_add(&inode->i_lru, &dispose);

/*
* We can have a ton of inodes to evict at unmount time given
* enough memory, check to see if we need to go to sleep for a
* bit so we don't livelock.
*/
if (need_resched()) {
spin_unlock(&sb->s_inode_list_lock);
cond_resched();
dispose_list(&dispose);
goto again;
}
}
spin_unlock(&sb->s_inode_list_lock);

sb_for_each_inodes(sb, flags, evict_inodes_inode_iter_cb, &dispose);
dispose_list(&dispose);
}
EXPORT_SYMBOL_GPL(evict_inodes);
Expand Down
Loading