iwlwifi: mvm: add trigger for firmware dump upon channel switch
[firefly-linux-kernel-4.4.55.git] / fs / ext4 / inode.c
index 28555f191b62b93ff5fd8a75de45d37aa9d7223e..85404f15e53a28860ce5a7be08220106b76fc06f 100644 (file)
@@ -4174,6 +4174,65 @@ static int ext4_inode_blocks_set(handle_t *handle,
        return 0;
 }
 
+struct other_inode {
+       unsigned long           orig_ino;
+       struct ext4_inode       *raw_inode;
+};
+
+static int other_inode_match(struct inode * inode, unsigned long ino,
+                            void *data)
+{
+       struct other_inode *oi = (struct other_inode *) data;
+
+       if ((inode->i_ino != ino) ||
+           (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW |
+                              I_DIRTY_SYNC | I_DIRTY_DATASYNC)) ||
+           ((inode->i_state & I_DIRTY_TIME) == 0))
+               return 0;
+       spin_lock(&inode->i_lock);
+       if (((inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW |
+                               I_DIRTY_SYNC | I_DIRTY_DATASYNC)) == 0) &&
+           (inode->i_state & I_DIRTY_TIME)) {
+               struct ext4_inode_info  *ei = EXT4_I(inode);
+
+               inode->i_state &= ~(I_DIRTY_TIME | I_DIRTY_TIME_EXPIRED);
+               spin_unlock(&inode->i_lock);
+
+               spin_lock(&ei->i_raw_lock);
+               EXT4_INODE_SET_XTIME(i_ctime, inode, oi->raw_inode);
+               EXT4_INODE_SET_XTIME(i_mtime, inode, oi->raw_inode);
+               EXT4_INODE_SET_XTIME(i_atime, inode, oi->raw_inode);
+               ext4_inode_csum_set(inode, oi->raw_inode, ei);
+               spin_unlock(&ei->i_raw_lock);
+               trace_ext4_other_inode_update_time(inode, oi->orig_ino);
+               return -1;
+       }
+       spin_unlock(&inode->i_lock);
+       return -1;
+}
+
+/*
+ * Opportunistically update the other time fields for other inodes in
+ * the same inode table block.
+ */
+static void ext4_update_other_inodes_time(struct super_block *sb,
+                                         unsigned long orig_ino, char *buf)
+{
+       struct other_inode oi;
+       unsigned long ino;
+       int i, inodes_per_block = EXT4_SB(sb)->s_inodes_per_block;
+       int inode_size = EXT4_INODE_SIZE(sb);
+
+       oi.orig_ino = orig_ino;
+       ino = orig_ino & ~(inodes_per_block - 1);
+       for (i = 0; i < inodes_per_block; i++, ino++, buf += inode_size) {
+               if (ino == orig_ino)
+                       continue;
+               oi.raw_inode = (struct ext4_inode *) buf;
+               (void) find_inode_nowait(sb, ino, other_inode_match, &oi);
+       }
+}
+
 /*
  * Post the struct inode info into an on-disk inode location in the
  * buffer-cache.  This gobbles the caller's reference to the
@@ -4283,10 +4342,11 @@ static int ext4_do_update_inode(handle_t *handle,
                                cpu_to_le16(ei->i_extra_isize);
                }
        }
-
        ext4_inode_csum_set(inode, raw_inode, ei);
-
        spin_unlock(&ei->i_raw_lock);
+       if (inode->i_sb->s_flags & MS_LAZYTIME)
+               ext4_update_other_inodes_time(inode->i_sb, inode->i_ino,
+                                             bh->b_data);
 
        BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
        rc = ext4_handle_dirty_metadata(handle, NULL, bh);
@@ -4875,11 +4935,17 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
  * 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;