aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/fs-writeback.c82
-rw-r--r--include/linux/writeback.h2
2 files changed, 46 insertions, 38 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 94a602e98bb..8cc06d5432b 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -554,29 +554,41 @@ static bool pin_sb_for_writeback(struct super_block *sb)
/*
* Write a portion of b_io inodes which belong to @sb.
- * If @wbc->sb != NULL, then find and write all such
+ *
+ * If @only_this_sb is true, then find and write all such
* inodes. Otherwise write only ones which go sequentially
* in reverse order.
+ *
* Return 1, if the caller writeback routine should be
* interrupted. Otherwise return 0.
*/
-static int writeback_sb_inodes(struct super_block *sb,
- struct bdi_writeback *wb,
- struct writeback_control *wbc)
+static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb,
+ struct writeback_control *wbc, bool only_this_sb)
{
while (!list_empty(&wb->b_io)) {
long pages_skipped;
struct inode *inode = list_entry(wb->b_io.prev,
struct inode, i_list);
- if (wbc->sb && sb != inode->i_sb) {
- /* super block given and doesn't
- match, skip this inode */
- redirty_tail(inode);
- continue;
- }
- if (sb != inode->i_sb)
- /* finish with this superblock */
+
+ if (inode->i_sb != sb) {
+ if (only_this_sb) {
+ /*
+ * We only want to write back data for this
+ * superblock, move all inodes not belonging
+ * to it back onto the dirty list.
+ */
+ redirty_tail(inode);
+ continue;
+ }
+
+ /*
+ * The inode belongs to a different superblock.
+ * Bounce back to the caller to unpin this and
+ * pin the next superblock.
+ */
return 0;
+ }
+
if (inode->i_state & (I_NEW | I_WILL_FREE)) {
requeue_io(inode);
continue;
@@ -629,29 +641,12 @@ void writeback_inodes_wb(struct bdi_writeback *wb,
struct inode, i_list);
struct super_block *sb = inode->i_sb;
- if (wbc->sb) {
- /*
- * We are requested to write out inodes for a specific
- * superblock. This means we already have s_umount
- * taken by the caller which also waits for us to
- * complete the writeout.
- */
- if (sb != wbc->sb) {
- redirty_tail(inode);
- continue;
- }
-
- WARN_ON(!rwsem_is_locked(&sb->s_umount));
-
- ret = writeback_sb_inodes(sb, wb, wbc);
- } else {
- if (!pin_sb_for_writeback(sb)) {
- requeue_io(inode);
- continue;
- }
- ret = writeback_sb_inodes(sb, wb, wbc);
- drop_super(sb);
+ if (!pin_sb_for_writeback(sb)) {
+ requeue_io(inode);
+ continue;
}
+ ret = writeback_sb_inodes(sb, wb, wbc, false);
+ drop_super(sb);
if (ret)
break;
@@ -660,6 +655,19 @@ void writeback_inodes_wb(struct bdi_writeback *wb,
/* Leave any unwritten inodes on b_io */
}
+static void __writeback_inodes_sb(struct super_block *sb,
+ struct bdi_writeback *wb, struct writeback_control *wbc)
+{
+ WARN_ON(!rwsem_is_locked(&sb->s_umount));
+
+ wbc->wb_start = jiffies; /* livelock avoidance */
+ spin_lock(&inode_lock);
+ if (!wbc->for_kupdate || list_empty(&wb->b_io))
+ queue_io(wb, wbc->older_than_this);
+ writeback_sb_inodes(sb, wb, wbc, true);
+ spin_unlock(&inode_lock);
+}
+
/*
* The maximum number of pages to writeout in a single bdi flush/kupdate
* operation. We do this so we don't hold I_SYNC against an inode for
@@ -698,7 +706,6 @@ static long wb_writeback(struct bdi_writeback *wb,
struct wb_writeback_args *args)
{
struct writeback_control wbc = {
- .sb = args->sb,
.sync_mode = args->sync_mode,
.older_than_this = NULL,
.for_kupdate = args->for_kupdate,
@@ -736,7 +743,10 @@ static long wb_writeback(struct bdi_writeback *wb,
wbc.more_io = 0;
wbc.nr_to_write = MAX_WRITEBACK_PAGES;
wbc.pages_skipped = 0;
- writeback_inodes_wb(wb, &wbc);
+ if (args->sb)
+ __writeback_inodes_sb(args->sb, wb, &wbc);
+ else
+ writeback_inodes_wb(wb, &wbc);
args->nr_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;
wrote += MAX_WRITEBACK_PAGES - wbc.nr_to_write;
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index f6756f6a610..c24eca71e80 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -27,8 +27,6 @@ enum writeback_sync_modes {
* in a manner such that unspecified fields are set to zero.
*/
struct writeback_control {
- struct super_block *sb; /* if !NULL, only write inodes from
- this super_block */
enum writeback_sync_modes sync_mode;
unsigned long *older_than_this; /* If !NULL, only write back inodes
older than this */