* If the inode is marked synchronous, we don't honour that here - doing
  * so would cause a commit on atime updates, which we don't bother doing.
  * We handle synchronous inodes at the highest possible level.
+ *
+ * If only the I_DIRTY_TIME flag is set, we can skip everything.  If
+ * I_DIRTY_TIME and I_DIRTY_SYNC is set, the only inode fields we need
+ * to copy into the on-disk inode structure are the timestamp files.
  */
 void ext4_dirty_inode(struct inode *inode, int flags)
 {
        handle_t *handle;
 
+       if (flags == I_DIRTY_TIME)
+               return;
        handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
        if (IS_ERR(handle))
                goto out;
 
        return ret;
 }
 
+#define EXPIRE_DIRTY_ATIME 0x0001
+
 /*
  * Move expired (dirtied before work->older_than_this) dirty inodes from
  * @delaying_queue to @dispatch_queue.
  */
 static int move_expired_inodes(struct list_head *delaying_queue,
                               struct list_head *dispatch_queue,
+                              int flags,
                               struct wb_writeback_work *work)
 {
+       unsigned long *older_than_this = NULL;
+       unsigned long expire_time;
        LIST_HEAD(tmp);
        struct list_head *pos, *node;
        struct super_block *sb = NULL;
        int do_sb_sort = 0;
        int moved = 0;
 
+       if ((flags & EXPIRE_DIRTY_ATIME) == 0)
+               older_than_this = work->older_than_this;
+       else if ((work->reason == WB_REASON_SYNC) == 0) {
+               expire_time = jiffies - (HZ * 86400);
+               older_than_this = &expire_time;
+       }
        while (!list_empty(delaying_queue)) {
                inode = wb_inode(delaying_queue->prev);
-               if (work->older_than_this &&
-                   inode_dirtied_after(inode, *work->older_than_this))
+               if (older_than_this &&
+                   inode_dirtied_after(inode, *older_than_this))
                        break;
                list_move(&inode->i_wb_list, &tmp);
                moved++;
+               if (flags & EXPIRE_DIRTY_ATIME)
+                       set_bit(__I_DIRTY_TIME_EXPIRED, &inode->i_state);
                if (sb_is_blkdev_sb(inode->i_sb))
                        continue;
                if (sb && sb != inode->i_sb)
 static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work)
 {
        int moved;
+
        assert_spin_locked(&wb->list_lock);
        list_splice_init(&wb->b_more_io, &wb->b_io);
-       moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, work);
+       moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, 0, work);
+       moved += move_expired_inodes(&wb->b_dirty_time, &wb->b_io,
+                                    EXPIRE_DIRTY_ATIME, work);
        trace_writeback_queue_io(wb, work, moved);
 }
 
                 * updates after data IO completion.
                 */
                redirty_tail(inode, wb);
+       } else if (inode->i_state & I_DIRTY_TIME) {
+               list_move(&inode->i_wb_list, &wb->b_dirty_time);
        } else {
                /* The inode is clean. Remove from writeback lists. */
                list_del_init(&inode->i_wb_list);
        spin_lock(&inode->i_lock);
 
        dirty = inode->i_state & I_DIRTY;
-       inode->i_state &= ~I_DIRTY;
+       if (((dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) &&
+            (inode->i_state & I_DIRTY_TIME)) ||
+           (inode->i_state & I_DIRTY_TIME_EXPIRED)) {
+               dirty |= I_DIRTY_TIME | I_DIRTY_TIME_EXPIRED;
+               trace_writeback_lazytime(inode);
+       }
+       inode->i_state &= ~dirty;
 
        /*
         * Paired with smp_mb() in __mark_inode_dirty().  This allows
 
        spin_unlock(&inode->i_lock);
 
+       if (dirty & I_DIRTY_TIME)
+               mark_inode_dirty_sync(inode);
        /* Don't write the inode if only I_DIRTY_PAGES was set */
-       if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) {
+       if (dirty & ~I_DIRTY_PAGES) {
                int err = write_inode(inode, wbc);
                if (ret == 0)
                        ret = err;
         * make sure inode is on some writeback list and leave it there unless
         * we have completely cleaned the inode.
         */
-       if (!(inode->i_state & I_DIRTY) &&
+       if (!(inode->i_state & I_DIRTY_ALL) &&
            (wbc->sync_mode != WB_SYNC_ALL ||
             !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK)))
                goto out;
         * If inode is clean, remove it from writeback lists. Otherwise don't
         * touch it. See comment above for explanation.
         */
-       if (!(inode->i_state & I_DIRTY))
+       if (!(inode->i_state & I_DIRTY_ALL))
                list_del_init(&inode->i_wb_list);
        spin_unlock(&wb->list_lock);
        inode_sync_complete(inode);
                wrote += write_chunk - wbc.nr_to_write;
                spin_lock(&wb->list_lock);
                spin_lock(&inode->i_lock);
-               if (!(inode->i_state & I_DIRTY))
+               if (!(inode->i_state & I_DIRTY_ALL))
                        wrote++;
                requeue_inode(inode, wb, &wbc);
                inode_sync_complete(inode);
  * page->mapping->host, so the page-dirtying time is recorded in the internal
  * blockdev inode.
  */
+#define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC)
 void __mark_inode_dirty(struct inode *inode, int flags)
 {
        struct super_block *sb = inode->i_sb;
        struct backing_dev_info *bdi = NULL;
+       int dirtytime;
+
+       trace_writeback_mark_inode_dirty(inode, flags);
 
        /*
         * Don't do this for I_DIRTY_PAGES - that doesn't actually
         * dirty the inode itself
         */
-       if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) {
+       if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_TIME)) {
                trace_writeback_dirty_inode_start(inode, flags);
 
                if (sb->s_op->dirty_inode)
 
                trace_writeback_dirty_inode(inode, flags);
        }
+       if (flags & I_DIRTY_INODE)
+               flags &= ~I_DIRTY_TIME;
+       dirtytime = flags & I_DIRTY_TIME;
 
        /*
         * Paired with smp_mb() in __writeback_single_inode() for the
         */
        smp_mb();
 
-       if ((inode->i_state & flags) == flags)
+       if (((inode->i_state & flags) == flags) ||
+           (dirtytime && (inode->i_state & I_DIRTY_INODE)))
                return;
 
        if (unlikely(block_dump))
                block_dump___mark_inode_dirty(inode);
 
        spin_lock(&inode->i_lock);
+       if (dirtytime && (inode->i_state & I_DIRTY_INODE))
+               goto out_unlock_inode;
        if ((inode->i_state & flags) != flags) {
                const int was_dirty = inode->i_state & I_DIRTY;
 
+               if (flags & I_DIRTY_INODE)
+                       inode->i_state &= ~I_DIRTY_TIME;
                inode->i_state |= flags;
 
                /*
                        }
 
                        inode->dirtied_when = jiffies;
-                       list_move(&inode->i_wb_list, &bdi->wb.b_dirty);
+                       list_move(&inode->i_wb_list, dirtytime ?
+                                 &bdi->wb.b_dirty_time : &bdi->wb.b_dirty);
                        spin_unlock(&bdi->wb.list_lock);
+                       trace_writeback_dirty_inode_enqueue(inode);
 
                        if (wakeup_bdi)
                                bdi_wakeup_thread_delayed(bdi);
 
 {
        struct address_space *mapping = file->f_mapping;
        struct inode *inode = mapping->host;
-       int sync_state = inode->i_state & I_DIRTY;
+       int sync_state = inode->i_state & I_DIRTY_ALL;
        struct gfs2_inode *ip = GFS2_I(inode);
        int ret = 0, ret1 = 0;
 
        if (!gfs2_is_jdata(ip))
                sync_state &= ~I_DIRTY_PAGES;
        if (datasync)
-               sync_state &= ~I_DIRTY_SYNC;
+               sync_state &= ~(I_DIRTY_SYNC | I_DIRTY_TIME);
 
        if (sync_state) {
                ret = sync_inode_metadata(inode, 1);
 
 #include <linux/buffer_head.h> /* for inode_has_buffers */
 #include <linux/ratelimit.h>
 #include <linux/list_lru.h>
+#include <trace/events/writeback.h>
 #include "internal.h"
 
 /*
  * inode_sb_list_lock protects:
  *   sb->s_inodes, inode->i_sb_list
  * bdi->wb.list_lock protects:
- *   bdi->wb.b_{dirty,io,more_io}, inode->i_wb_list
+ *   bdi->wb.b_{dirty,io,more_io,dirty_time}, inode->i_wb_list
  * inode_hash_lock protects:
  *   inode_hashtable, inode->i_hash
  *
  */
 void inode_add_lru(struct inode *inode)
 {
-       if (!(inode->i_state & (I_DIRTY | I_SYNC | I_FREEING | I_WILL_FREE)) &&
+       if (!(inode->i_state & (I_DIRTY_ALL | I_SYNC |
+                               I_FREEING | I_WILL_FREE)) &&
            !atomic_read(&inode->i_count) && inode->i_sb->s_flags & MS_ACTIVE)
                inode_lru_list_add(inode);
 }
                        spin_unlock(&inode->i_lock);
                        continue;
                }
-               if (inode->i_state & I_DIRTY && !kill_dirty) {
+               if (inode->i_state & I_DIRTY_ALL && !kill_dirty) {
                        spin_unlock(&inode->i_lock);
                        busy = 1;
                        continue;
  */
 void iput(struct inode *inode)
 {
-       if (inode) {
-               BUG_ON(inode->i_state & I_CLEAR);
-
-               if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock))
-                       iput_final(inode);
+       if (!inode)
+               return;
+       BUG_ON(inode->i_state & I_CLEAR);
+retry:
+       if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock)) {
+               if (inode->i_nlink && (inode->i_state & I_DIRTY_TIME)) {
+                       atomic_inc(&inode->i_count);
+                       inode->i_state &= ~I_DIRTY_TIME;
+                       spin_unlock(&inode->i_lock);
+                       trace_writeback_lazytime_iput(inode);
+                       mark_inode_dirty_sync(inode);
+                       goto retry;
+               }
+               iput_final(inode);
        }
 }
 EXPORT_SYMBOL(iput);
        return 0;
 }
 
-/*
- * This does the actual work of updating an inodes time or version.  Must have
- * had called mnt_want_write() before calling this.
- */
-static int update_time(struct inode *inode, struct timespec *time, int flags)
+int generic_update_time(struct inode *inode, struct timespec *time, int flags)
 {
-       if (inode->i_op->update_time)
-               return inode->i_op->update_time(inode, time, flags);
+       int iflags = I_DIRTY_TIME;
 
        if (flags & S_ATIME)
                inode->i_atime = *time;
                inode->i_ctime = *time;
        if (flags & S_MTIME)
                inode->i_mtime = *time;
-       mark_inode_dirty_sync(inode);
+
+       if (!(inode->i_sb->s_flags & MS_LAZYTIME) || (flags & S_VERSION))
+               iflags |= I_DIRTY_SYNC;
+       __mark_inode_dirty(inode, iflags);
        return 0;
 }
+EXPORT_SYMBOL(generic_update_time);
+
+/*
+ * This does the actual work of updating an inodes time or version.  Must have
+ * had called mnt_want_write() before calling this.
+ */
+static int update_time(struct inode *inode, struct timespec *time, int flags)
+{
+       int (*update_time)(struct inode *, struct timespec *, int);
+
+       update_time = inode->i_op->update_time ? inode->i_op->update_time :
+               generic_update_time;
+
+       return update_time(inode, time, flags);
+}
 
 /**
  *     touch_atime     -       update the access time
 
                return rc;
 
        mutex_lock(&inode->i_mutex);
-       if (!(inode->i_state & I_DIRTY) ||
+       if (!(inode->i_state & I_DIRTY_ALL) ||
            (datasync && !(inode->i_state & I_DIRTY_DATASYNC))) {
                /* Make sure committed changes hit the disk */
                jfs_flush_journal(JFS_SBI(inode->i_sb)->log, 1);
 
 
        mutex_lock(&inode->i_mutex);
        ret = sync_mapping_buffers(inode->i_mapping);
-       if (!(inode->i_state & I_DIRTY))
+       if (!(inode->i_state & I_DIRTY_ALL))
                goto out;
        if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
                goto out;
 
                { MS_SYNCHRONOUS, ",sync" },
                { MS_DIRSYNC, ",dirsync" },
                { MS_MANDLOCK, ",mand" },
+               { MS_LAZYTIME, ",lazytime" },
                { 0, NULL }
        };
        const struct proc_fs_info *fs_infop;
 
  */
 int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync)
 {
+       struct inode *inode = file->f_mapping->host;
+
        if (!file->f_op->fsync)
                return -EINVAL;
+       if (!datasync && (inode->i_state & I_DIRTY_TIME)) {
+               spin_lock(&inode->i_lock);
+               inode->i_state &= ~I_DIRTY_TIME;
+               spin_unlock(&inode->i_lock);
+               mark_inode_dirty_sync(inode);
+       }
        return file->f_op->fsync(file, start, end, datasync);
 }
 EXPORT_SYMBOL(vfs_fsync_range);
 
        struct list_head b_dirty;       /* dirty inodes */
        struct list_head b_io;          /* parked for writeback */
        struct list_head b_more_io;     /* parked for more writeback */
+       struct list_head b_dirty_time;  /* time stamps are dirty */
        spinlock_t list_lock;           /* protects the b_* lists */
 };
 
 
 #define __I_DIO_WAKEUP         9
 #define I_DIO_WAKEUP           (1 << I_DIO_WAKEUP)
 #define I_LINKABLE             (1 << 10)
+#define I_DIRTY_TIME           (1 << 11)
+#define __I_DIRTY_TIME_EXPIRED 12
+#define I_DIRTY_TIME_EXPIRED   (1 << __I_DIRTY_TIME_EXPIRED)
 
 #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
+#define I_DIRTY_ALL (I_DIRTY | I_DIRTY_TIME)
 
 extern void __mark_inode_dirty(struct inode *, int);
 static inline void mark_inode_dirty(struct inode *inode)
 
 extern void ihold(struct inode * inode);
 extern void iput(struct inode *);
+extern int generic_update_time(struct inode *, struct timespec *, int);
 
 static inline struct inode *file_inode(const struct file *f)
 {
 
                {I_FREEING,             "I_FREEING"},           \
                {I_CLEAR,               "I_CLEAR"},             \
                {I_SYNC,                "I_SYNC"},              \
+               {I_DIRTY_TIME,          "I_DIRTY_TIME"},        \
+               {I_DIRTY_TIME_EXPIRED,  "I_DIRTY_TIME_EXPIRED"}, \
                {I_REFERENCED,          "I_REFERENCED"}         \
        )
 
        TP_STRUCT__entry (
                __array(char, name, 32)
                __field(unsigned long, ino)
+               __field(unsigned long, state)
                __field(unsigned long, flags)
        ),
 
                strncpy(__entry->name,
                        bdi->dev ? dev_name(bdi->dev) : "(unknown)", 32);
                __entry->ino            = inode->i_ino;
+               __entry->state          = inode->i_state;
                __entry->flags          = flags;
        ),
 
-       TP_printk("bdi %s: ino=%lu flags=%s",
+       TP_printk("bdi %s: ino=%lu state=%s flags=%s",
                __entry->name,
                __entry->ino,
+               show_inode_state(__entry->state),
                show_inode_state(__entry->flags)
        )
 );
 
+DEFINE_EVENT(writeback_dirty_inode_template, writeback_mark_inode_dirty,
+
+       TP_PROTO(struct inode *inode, int flags),
+
+       TP_ARGS(inode, flags)
+);
+
 DEFINE_EVENT(writeback_dirty_inode_template, writeback_dirty_inode_start,
 
        TP_PROTO(struct inode *inode, int flags),
        TP_ARGS(inode, wbc, nr_to_write)
 );
 
+DECLARE_EVENT_CLASS(writeback_lazytime_template,
+       TP_PROTO(struct inode *inode),
+
+       TP_ARGS(inode),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(unsigned long,  ino                     )
+               __field(unsigned long,  state                   )
+               __field(        __u16, mode                     )
+               __field(unsigned long, dirtied_when             )
+       ),
+
+       TP_fast_assign(
+               __entry->dev    = inode->i_sb->s_dev;
+               __entry->ino    = inode->i_ino;
+               __entry->state  = inode->i_state;
+               __entry->mode   = inode->i_mode;
+               __entry->dirtied_when = inode->dirtied_when;
+       ),
+
+       TP_printk("dev %d,%d ino %lu dirtied %lu state %s mode 0%o",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino, __entry->dirtied_when,
+                 show_inode_state(__entry->state), __entry->mode)
+);
+
+DEFINE_EVENT(writeback_lazytime_template, writeback_lazytime,
+       TP_PROTO(struct inode *inode),
+
+       TP_ARGS(inode)
+);
+
+DEFINE_EVENT(writeback_lazytime_template, writeback_lazytime_iput,
+       TP_PROTO(struct inode *inode),
+
+       TP_ARGS(inode)
+);
+
+DEFINE_EVENT(writeback_lazytime_template, writeback_dirty_inode_enqueue,
+
+       TP_PROTO(struct inode *inode),
+
+       TP_ARGS(inode)
+);
+
 #endif /* _TRACE_WRITEBACK_H */
 
 /* This part must be outside protection */
 
 #define MS_KERNMOUNT   (1<<22) /* this is a kern_mount call */
 #define MS_I_VERSION   (1<<23) /* Update inode I_version field */
 #define MS_STRICTATIME (1<<24) /* Always perform atime updates */
+#define MS_LAZYTIME    (1<<25) /* Update the on-disk [acm]times lazily */
 
 /* These sb flags are internal to the kernel */
 #define MS_NOSEC       (1<<28)
 /*
  * Superblock flags that can be altered by MS_REMOUNT
  */
-#define MS_RMT_MASK    (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION)
+#define MS_RMT_MASK    (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|\
+                        MS_LAZYTIME)
 
 /*
  * Old magic mount flag and mask
 
        unsigned long background_thresh;
        unsigned long dirty_thresh;
        unsigned long bdi_thresh;
-       unsigned long nr_dirty, nr_io, nr_more_io;
+       unsigned long nr_dirty, nr_io, nr_more_io, nr_dirty_time;
        struct inode *inode;
 
-       nr_dirty = nr_io = nr_more_io = 0;
+       nr_dirty = nr_io = nr_more_io = nr_dirty_time = 0;
        spin_lock(&wb->list_lock);
        list_for_each_entry(inode, &wb->b_dirty, i_wb_list)
                nr_dirty++;
                nr_io++;
        list_for_each_entry(inode, &wb->b_more_io, i_wb_list)
                nr_more_io++;
+       list_for_each_entry(inode, &wb->b_dirty_time, i_wb_list)
+               if (inode->i_state & I_DIRTY_TIME)
+                       nr_dirty_time++;
        spin_unlock(&wb->list_lock);
 
        global_dirty_limits(&background_thresh, &dirty_thresh);
                   "b_dirty:            %10lu\n"
                   "b_io:               %10lu\n"
                   "b_more_io:          %10lu\n"
+                  "b_dirty_time:       %10lu\n"
                   "bdi_list:           %10u\n"
                   "state:              %10lx\n",
                   (unsigned long) K(bdi_stat(bdi, BDI_WRITEBACK)),
                   nr_dirty,
                   nr_io,
                   nr_more_io,
+                  nr_dirty_time,
                   !list_empty(&bdi->bdi_list), bdi->state);
 #undef K
 
        INIT_LIST_HEAD(&wb->b_dirty);
        INIT_LIST_HEAD(&wb->b_io);
        INIT_LIST_HEAD(&wb->b_more_io);
+       INIT_LIST_HEAD(&wb->b_dirty_time);
        spin_lock_init(&wb->list_lock);
        INIT_DELAYED_WORK(&wb->dwork, bdi_writeback_workfn);
 }