OSDN Git Service

partitions: fix sometimes unreadable partition strings
[android-x86/kernel.git] / fs / fs-writeback.c
index 002be0f..2f76c4a 100644 (file)
@@ -76,23 +76,19 @@ static void bdi_queue_work(struct backing_dev_info *bdi,
 {
        trace_writeback_queue(bdi, work);
 
-       spin_lock(&bdi->wb_lock);
+       spin_lock_bh(&bdi->wb_lock);
        list_add_tail(&work->list, &bdi->work_list);
-       spin_unlock(&bdi->wb_lock);
-
-       /*
-        * If the default thread isn't there, make sure we add it. When
-        * it gets created and wakes up, we'll run this work.
-        */
-       if (unlikely(!bdi->wb.task)) {
+       if (bdi->wb.task) {
+               wake_up_process(bdi->wb.task);
+       } else {
+               /*
+                * The bdi thread isn't there, wake up the forker thread which
+                * will create and run it.
+                */
                trace_writeback_nothread(bdi, work);
                wake_up_process(default_backing_dev_info.wb.task);
-       } else {
-               struct bdi_writeback *wb = &bdi->wb;
-
-               if (wb->task)
-                       wake_up_process(wb->task);
        }
+       spin_unlock_bh(&bdi->wb_lock);
 }
 
 static void
@@ -366,7 +362,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
 
        spin_lock(&inode_lock);
        inode->i_state &= ~I_SYNC;
-       if (!(inode->i_state & (I_FREEING | I_CLEAR))) {
+       if (!(inode->i_state & I_FREEING)) {
                if ((inode->i_state & I_DIRTY_PAGES) && wbc->for_kupdate) {
                        /*
                         * More pages get dirtied by a fast dirtier.
@@ -513,7 +509,7 @@ static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb,
                if (inode_dirtied_after(inode, wbc->wb_start))
                        return 1;
 
-               BUG_ON(inode->i_state & (I_FREEING | I_CLEAR));
+               BUG_ON(inode->i_state & I_FREEING);
                __iget(inode);
                pages_skipped = wbc->pages_skipped;
                writeback_single_inode(inode, wbc);
@@ -544,7 +540,8 @@ void writeback_inodes_wb(struct bdi_writeback *wb,
 {
        int ret = 0;
 
-       wbc->wb_start = jiffies; /* livelock avoidance */
+       if (!wbc->wb_start)
+               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);
@@ -573,7 +570,6 @@ static void __writeback_inodes_sb(struct super_block *sb,
 {
        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);
@@ -639,6 +635,7 @@ static long wb_writeback(struct bdi_writeback *wb,
                wbc.range_end = LLONG_MAX;
        }
 
+       wbc.wb_start = jiffies; /* livelock avoidance */
        for (;;) {
                /*
                 * Stop writeback when nr_pages has been consumed
@@ -708,13 +705,13 @@ get_next_work_item(struct backing_dev_info *bdi)
 {
        struct wb_writeback_work *work = NULL;
 
-       spin_lock(&bdi->wb_lock);
+       spin_lock_bh(&bdi->wb_lock);
        if (!list_empty(&bdi->work_list)) {
                work = list_entry(bdi->work_list.next,
                                  struct wb_writeback_work, list);
                list_del_init(&work->list);
        }
-       spin_unlock(&bdi->wb_lock);
+       spin_unlock_bh(&bdi->wb_lock);
        return work;
 }
 
@@ -800,74 +797,54 @@ int bdi_writeback_thread(void *data)
 {
        struct bdi_writeback *wb = data;
        struct backing_dev_info *bdi = wb->bdi;
-       unsigned long last_active = jiffies;
-       unsigned long wait_jiffies = -1UL;
        long pages_written;
 
-       /*
-        * Add us to the active bdi_list
-        */
-       spin_lock_bh(&bdi_lock);
-       list_add_rcu(&bdi->bdi_list, &bdi_list);
-       spin_unlock_bh(&bdi_lock);
-
        current->flags |= PF_FLUSHER | PF_SWAPWRITE;
        set_freezable();
+       wb->last_active = jiffies;
 
        /*
         * Our parent may run at a different priority, just set us to normal
         */
        set_user_nice(current, 0);
 
-       /*
-        * Clear pending bit and wakeup anybody waiting to tear us down
-        */
-       clear_bit(BDI_pending, &bdi->state);
-       smp_mb__after_clear_bit();
-       wake_up_bit(&bdi->state, BDI_pending);
-
        trace_writeback_thread_start(bdi);
 
        while (!kthread_should_stop()) {
+               /*
+                * Remove own delayed wake-up timer, since we are already awake
+                * and we'll take care of the preriodic write-back.
+                */
+               del_timer(&wb->wakeup_timer);
+
                pages_written = wb_do_writeback(wb, 0);
 
                trace_writeback_pages_written(pages_written);
 
                if (pages_written)
-                       last_active = jiffies;
-               else if (wait_jiffies != -1UL) {
-                       unsigned long max_idle;
+                       wb->last_active = jiffies;
 
-                       /*
-                        * Longest period of inactivity that we tolerate. If we
-                        * see dirty data again later, the thread will get
-                        * recreated automatically.
-                        */
-                       max_idle = max(5UL * 60 * HZ, wait_jiffies);
-                       if (time_after(jiffies, max_idle + last_active))
-                               break;
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (!list_empty(&bdi->work_list)) {
+                       __set_current_state(TASK_RUNNING);
+                       continue;
                }
 
-               if (dirty_writeback_interval) {
-                       wait_jiffies = msecs_to_jiffies(dirty_writeback_interval * 10);
-                       schedule_timeout_interruptible(wait_jiffies);
-               } else {
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       if (list_empty_careful(&wb->bdi->work_list) &&
-                           !kthread_should_stop())
-                               schedule();
-                       __set_current_state(TASK_RUNNING);
+               if (wb_has_dirty_io(wb) && dirty_writeback_interval)
+                       schedule_timeout(msecs_to_jiffies(dirty_writeback_interval * 10));
+               else {
+                       /*
+                        * We have nothing to do, so can go sleep without any
+                        * timeout and save power. When a work is queued or
+                        * something is made dirty - we will be woken up.
+                        */
+                       schedule();
                }
 
                try_to_freeze();
        }
 
-       wb->task = NULL;
-
-       /*
-        * Flush any work that raced with us exiting. No new work
-        * will be added, since this bdi isn't discoverable anymore.
-        */
+       /* Flush any work that raced with us exiting */
        if (!list_empty(&bdi->work_list))
                wb_do_writeback(wb, 1);
 
@@ -950,6 +927,8 @@ static noinline void block_dump___mark_inode_dirty(struct inode *inode)
 void __mark_inode_dirty(struct inode *inode, int flags)
 {
        struct super_block *sb = inode->i_sb;
+       struct backing_dev_info *bdi = NULL;
+       bool wakeup_bdi = false;
 
        /*
         * Don't do this for I_DIRTY_PAGES - that doesn't actually
@@ -995,7 +974,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)
                        if (hlist_unhashed(&inode->i_hash))
                                goto out;
                }
-               if (inode->i_state & (I_FREEING|I_CLEAR))
+               if (inode->i_state & I_FREEING)
                        goto out;
 
                /*
@@ -1003,22 +982,31 @@ void __mark_inode_dirty(struct inode *inode, int flags)
                 * reposition it (that would break b_dirty time-ordering).
                 */
                if (!was_dirty) {
-                       struct bdi_writeback *wb = &inode_to_bdi(inode)->wb;
-                       struct backing_dev_info *bdi = wb->bdi;
-
-                       if (bdi_cap_writeback_dirty(bdi) &&
-                           !test_bit(BDI_registered, &bdi->state)) {
-                               WARN_ON(1);
-                               printk(KERN_ERR "bdi-%s not registered\n",
-                                                               bdi->name);
+                       bdi = inode_to_bdi(inode);
+
+                       if (bdi_cap_writeback_dirty(bdi)) {
+                               WARN(!test_bit(BDI_registered, &bdi->state),
+                                    "bdi-%s not registered\n", bdi->name);
+
+                               /*
+                                * If this is the first dirty inode for this
+                                * bdi, we have to wake-up the corresponding
+                                * bdi thread to make sure background
+                                * write-back happens later.
+                                */
+                               if (!wb_has_dirty_io(&bdi->wb))
+                                       wakeup_bdi = true;
                        }
 
                        inode->dirtied_when = jiffies;
-                       list_move(&inode->i_list, &wb->b_dirty);
+                       list_move(&inode->i_list, &bdi->wb.b_dirty);
                }
        }
 out:
        spin_unlock(&inode_lock);
+
+       if (wakeup_bdi)
+               bdi_wakeup_thread_delayed(bdi);
 }
 EXPORT_SYMBOL(__mark_inode_dirty);
 
@@ -1061,7 +1049,7 @@ static void wait_sb_inodes(struct super_block *sb)
        list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
                struct address_space *mapping;
 
-               if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE|I_NEW))
+               if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW))
                        continue;
                mapping = inode->i_mapping;
                if (mapping->nrpages == 0)