2006-09-13 16:42:13

by Alexandre Ratchov

[permalink] [raw]
Subject: rfc: [patch] change attribute for ext3

hello,

here is a small patch that adds the "change attribute" for ext3
file-systems;

the change attribute is a simple counter that is reset to zero on
inode creation and that is incremented every time the inode data is
modified (similarly to the "ctime" time-stamp).

Its purpose is to make possible to watch a file for changes, as
in the following pseudo-code:

stat(filename, oldsb);

... /* do something */

stat(filename, newsb);
if (oldsb->st_ctime != newsb->st_ctime ||
oldsb->st_change_attribute != newsb->st_changeattribute) {

/* file changed */
} else {

/* file didn't change */
}

The patch also adds a new ``st_change_attribute'' field in the stat
structure, and modifies the stat(2) syscall accordingly. Currently the
change is only visible on i386 and x86_64 archs.

In order to test the patch, there's a trivial utility that displays the
value of the change attribute:

http://www.bullopensource.org/ext4/20060913/chinfo.tar.gz

Comments?

cheers,

-- Alexandre


Signed-off-by: Celine Bourde
Signed-off-by: Alexandre Ratchov

Index: fs/attr.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/attr.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.10.1
diff -u -p -r1.1.1.1 -r1.1.1.1.10.1
--- fs/attr.c 28 Jun 2006 18:41:20 -0000 1.1.1.1
+++ fs/attr.c 13 Sep 2006 18:15:43 -0000 1.1.1.1.10.1
@@ -88,6 +88,9 @@ int inode_setattr(struct inode * inode,
if (ia_valid & ATTR_CTIME)
inode->i_ctime = timespec_trunc(attr->ia_ctime,
inode->i_sb->s_time_gran);
+ if (ia_valid & ATTR_CHANGE_ATTRIBUTE)
+ inode->i_change_attribute = attr->ia_change_attribute;
+
if (ia_valid & ATTR_MODE) {
umode_t mode = attr->ia_mode;

@@ -111,7 +114,7 @@ int notify_change(struct dentry * dentry

mode = inode->i_mode;
now = current_fs_time(inode->i_sb);
-
+ inode->i_change_attribute++;
attr->ia_ctime = now;
if (!(ia_valid & ATTR_ATIME_SET))
attr->ia_atime = now;
Index: fs/bad_inode.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/bad_inode.c,v
retrieving revision 1.1.1.2
retrieving revision 1.1.1.2.6.1
diff -u -p -r1.1.1.2 -r1.1.1.2.6.1
--- fs/bad_inode.c 28 Jun 2006 18:42:06 -0000 1.1.1.2
+++ fs/bad_inode.c 13 Sep 2006 18:15:43 -0000 1.1.1.2.6.1
@@ -97,6 +97,7 @@ void make_bad_inode(struct inode * inode
inode->i_mode = S_IFREG;
inode->i_atime = inode->i_mtime = inode->i_ctime =
current_fs_time(inode->i_sb);
+ inode->i_change_attribute++;
inode->i_op = &bad_inode_ops;
inode->i_fop = &bad_file_ops;
}
Index: fs/binfmt_misc.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/binfmt_misc.c,v
retrieving revision 1.1.1.3
retrieving revision 1.1.1.3.2.1
diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
--- fs/binfmt_misc.c 13 Sep 2006 17:45:04 -0000 1.1.1.3
+++ fs/binfmt_misc.c 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
@@ -511,6 +511,7 @@ static struct inode *bm_get_inode(struct
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime =
current_fs_time(inode->i_sb);
+ inode->i_change_attribute = 0;
}
return inode;
}
Index: fs/eventpoll.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/eventpoll.c,v
retrieving revision 1.1.1.3
retrieving revision 1.1.1.3.2.1
diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
--- fs/eventpoll.c 13 Sep 2006 17:45:04 -0000 1.1.1.3
+++ fs/eventpoll.c 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
@@ -1590,6 +1590,7 @@ static struct inode *ep_eventpoll_inode(
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_change_attribute = 0;
inode->i_blksize = PAGE_SIZE;
return inode;

Index: fs/inode.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/inode.c,v
retrieving revision 1.1.1.3
retrieving revision 1.1.1.3.2.1
diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
--- fs/inode.c 13 Sep 2006 17:45:04 -0000 1.1.1.3
+++ fs/inode.c 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
@@ -1232,16 +1232,10 @@ void file_update_time(struct file *file)
return;

now = current_fs_time(inode->i_sb);
- if (!timespec_equal(&inode->i_mtime, &now))
- sync_it = 1;
inode->i_mtime = now;
-
- if (!timespec_equal(&inode->i_ctime, &now))
- sync_it = 1;
inode->i_ctime = now;
-
- if (sync_it)
- mark_inode_dirty_sync(inode);
+ inode->i_change_attribute++;
+ mark_inode_dirty_sync(inode);
}

EXPORT_SYMBOL(file_update_time);
Index: fs/libfs.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/libfs.c,v
retrieving revision 1.1.1.3
retrieving revision 1.1.1.3.2.1
diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
--- fs/libfs.c 13 Sep 2006 17:45:04 -0000 1.1.1.3
+++ fs/libfs.c 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
@@ -220,6 +220,7 @@ int get_sb_pseudo(struct file_system_typ
root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;
root->i_uid = root->i_gid = 0;
root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME;
+ root->i_change_attribute = 0;
dentry = d_alloc(NULL, &d_name);
if (!dentry) {
iput(root);
@@ -242,6 +243,8 @@ int simple_link(struct dentry *old_dentr
{
struct inode *inode = old_dentry->d_inode;

+ inode->i_change_attribute++;
+ dir->i_change_attribute++;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
inode->i_nlink++;
atomic_inc(&inode->i_count);
@@ -274,6 +277,8 @@ int simple_unlink(struct inode *dir, str
{
struct inode *inode = dentry->d_inode;

+ inode->i_change_attribute++;
+ dir->i_change_attribute++;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
inode->i_nlink--;
dput(dentry);
@@ -311,7 +316,8 @@ int simple_rename(struct inode *old_dir,

old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime =
new_dir->i_mtime = inode->i_ctime = CURRENT_TIME;
-
+ old_dir->i_change_attribute++;
+ new_dir->i_change_attribute++;
return 0;
}

@@ -386,6 +392,7 @@ int simple_fill_super(struct super_block
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_change_attribute = 0;
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
inode->i_nlink = 2;
@@ -408,6 +415,7 @@ int simple_fill_super(struct super_block
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_change_attribute = 0;
inode->i_fop = files->ops;
inode->i_ino = i;
d_add(dentry, inode);
Index: fs/pipe.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/pipe.c,v
retrieving revision 1.1.1.3
retrieving revision 1.1.1.3.2.1
diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
--- fs/pipe.c 13 Sep 2006 17:45:04 -0000 1.1.1.3
+++ fs/pipe.c 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
@@ -879,6 +879,7 @@ static struct inode * get_pipe_inode(voi
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_change_attribute = 0;
inode->i_blksize = PAGE_SIZE;

return inode;
Index: fs/stat.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/stat.c,v
retrieving revision 1.1.1.3
retrieving revision 1.1.1.3.2.1
diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
--- fs/stat.c 13 Sep 2006 17:45:04 -0000 1.1.1.3
+++ fs/stat.c 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
@@ -30,6 +30,7 @@ void generic_fillattr(struct inode *inod
stat->atime = inode->i_atime;
stat->mtime = inode->i_mtime;
stat->ctime = inode->i_ctime;
+ stat->change_attribute = inode->i_change_attribute;
stat->size = i_size_read(inode);
stat->blocks = inode->i_blocks;
stat->blksize = inode->i_blksize;
@@ -228,6 +229,7 @@ static int cp_new_stat(struct kstat *sta
tmp.st_atime = stat->atime.tv_sec;
tmp.st_mtime = stat->mtime.tv_sec;
tmp.st_ctime = stat->ctime.tv_sec;
+ tmp.st_change_attribute = stat->change_attribute;
#ifdef STAT_HAVE_NSEC
tmp.st_atime_nsec = stat->atime.tv_nsec;
tmp.st_mtime_nsec = stat->mtime.tv_nsec;
@@ -359,6 +361,7 @@ static long cp_new_stat64(struct kstat *
tmp.st_mtime_nsec = stat->mtime.tv_nsec;
tmp.st_ctime = stat->ctime.tv_sec;
tmp.st_ctime_nsec = stat->ctime.tv_nsec;
+ tmp.st_change_attribute = stat->change_attribute;
tmp.st_size = stat->size;
tmp.st_blocks = stat->blocks;
tmp.st_blksize = stat->blksize;
Index: fs/ext3/ialloc.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/ext3/ialloc.c,v
retrieving revision 1.1.1.2
retrieving revision 1.1.1.2.2.1
diff -u -p -r1.1.1.2 -r1.1.1.2.2.1
--- fs/ext3/ialloc.c 13 Sep 2006 17:45:04 -0000 1.1.1.2
+++ fs/ext3/ialloc.c 13 Sep 2006 18:15:43 -0000 1.1.1.2.2.1
@@ -562,7 +562,8 @@ got:
inode->i_blksize = PAGE_SIZE;
inode->i_blocks = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
-
+ inode->i_change_attribute = 0;
+
memset(ei->i_data, 0, sizeof(ei->i_data));
ei->i_dir_start_lookup = 0;
ei->i_disksize = 0;
Index: fs/ext3/inode.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/ext3/inode.c,v
retrieving revision 1.1.1.3
retrieving revision 1.1.1.3.2.1
diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
--- fs/ext3/inode.c 13 Sep 2006 17:45:05 -0000 1.1.1.3
+++ fs/ext3/inode.c 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
@@ -729,6 +729,7 @@ static int ext3_splice_branch(handle_t *
/* We are done with atomic stuff, now do the rest of housekeeping */

inode->i_ctime = CURRENT_TIME_SEC;
+ inode->i_change_attribute++;
ext3_mark_inode_dirty(handle, inode);

/* had we spliced it onto indirect block? */
@@ -2371,6 +2372,7 @@ do_indirects:

mutex_unlock(&ei->truncate_mutex);
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
+ inode->i_change_attribute++;
ext3_mark_inode_dirty(handle, inode);

/*
@@ -2608,7 +2610,8 @@ void ext3_read_inode(struct inode * inod
inode->i_ctime.tv_sec = le32_to_cpu(raw_inode->i_ctime);
inode->i_mtime.tv_sec = le32_to_cpu(raw_inode->i_mtime);
inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_mtime.tv_nsec = 0;
-
+ inode->i_change_attribute = le32_to_cpu(raw_inode->i_chattr);
+
ei->i_state = 0;
ei->i_dir_start_lookup = 0;
ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
@@ -2765,6 +2768,7 @@ static int ext3_do_update_inode(handle_t
raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec);
raw_inode->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec);
raw_inode->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec);
+ raw_inode->i_chattr = cpu_to_le32(inode->i_change_attribute);
raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
raw_inode->i_flags = cpu_to_le32(ei->i_flags);
Index: fs/ext3/ioctl.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/ext3/ioctl.c,v
retrieving revision 1.1.1.3
retrieving revision 1.1.1.3.2.1
diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
--- fs/ext3/ioctl.c 13 Sep 2006 17:45:05 -0000 1.1.1.3
+++ fs/ext3/ioctl.c 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
@@ -96,6 +96,7 @@ int ext3_ioctl (struct inode * inode, st

ext3_set_inode_flags(inode);
inode->i_ctime = CURRENT_TIME_SEC;
+ inode->i_change_attribute++;

err = ext3_mark_iloc_dirty(handle, inode, &iloc);
flags_err:
@@ -133,6 +134,7 @@ flags_err:
err = ext3_reserve_inode_write(handle, inode, &iloc);
if (err == 0) {
inode->i_ctime = CURRENT_TIME_SEC;
+ inode->i_change_attribute++;
inode->i_generation = generation;
err = ext3_mark_iloc_dirty(handle, inode, &iloc);
}
Index: fs/ext3/namei.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/ext3/namei.c,v
retrieving revision 1.1.1.2
retrieving revision 1.1.1.2.2.1
diff -u -p -r1.1.1.2 -r1.1.1.2.2.1
--- fs/ext3/namei.c 13 Sep 2006 17:45:05 -0000 1.1.1.2
+++ fs/ext3/namei.c 13 Sep 2006 18:15:43 -0000 1.1.1.2.2.1
@@ -1275,6 +1275,7 @@ static int add_dirent_to_buf(handle_t *h
* and/or different from the directory change time.
*/
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
+ dir->i_change_attribute++;
ext3_update_dx_flag(dir);
dir->i_version++;
ext3_mark_inode_dirty(handle, dir);
@@ -2051,6 +2052,8 @@ static int ext3_rmdir (struct inode * di
inode->i_size = 0;
ext3_orphan_add(handle, inode);
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
+ inode->i_change_attribute++;
+ dir->i_change_attribute++;
ext3_mark_inode_dirty(handle, inode);
dir->i_nlink--;
ext3_update_dx_flag(dir);
@@ -2101,12 +2104,14 @@ static int ext3_unlink(struct inode * di
if (retval)
goto end_unlink;
dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
+ dir->i_change_attribute++;
ext3_update_dx_flag(dir);
ext3_mark_inode_dirty(handle, dir);
inode->i_nlink--;
if (!inode->i_nlink)
ext3_orphan_add(handle, inode);
inode->i_ctime = dir->i_ctime;
+ inode->i_change_attribute++;
ext3_mark_inode_dirty(handle, inode);
retval = 0;

@@ -2192,6 +2197,7 @@ retry:
handle->h_sync = 1;

inode->i_ctime = CURRENT_TIME_SEC;
+ inode->i_change_attribute++;
ext3_inc_count(handle, inode);
atomic_inc(&inode->i_count);

@@ -2294,6 +2300,7 @@ static int ext3_rename (struct inode * o
* rename.
*/
old_inode->i_ctime = CURRENT_TIME_SEC;
+ old_inode->i_change_attribute++;
ext3_mark_inode_dirty(handle, old_inode);

/*
@@ -2327,8 +2334,10 @@ static int ext3_rename (struct inode * o
if (new_inode) {
new_inode->i_nlink--;
new_inode->i_ctime = CURRENT_TIME_SEC;
+ new_inode->i_change_attribute++;
}
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
+ old_dir->i_change_attribute++;
ext3_update_dx_flag(old_dir);
if (dir_bh) {
BUFFER_TRACE(dir_bh, "get_write_access");
Index: fs/ext3/super.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/ext3/super.c,v
retrieving revision 1.1.1.3
retrieving revision 1.1.1.3.2.1
diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
--- fs/ext3/super.c 13 Sep 2006 17:45:05 -0000 1.1.1.3
+++ fs/ext3/super.c 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
@@ -2656,6 +2656,7 @@ out:
}
inode->i_version++;
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_change_attribute = 0;
ext3_mark_inode_dirty(handle, inode);
mutex_unlock(&inode->i_mutex);
return len - towrite;
Index: fs/ext3/xattr.c
===================================================================
RCS file: /home/ratchova/cvs/linux/fs/ext3/xattr.c,v
retrieving revision 1.1.1.2
retrieving revision 1.1.1.2.2.1
diff -u -p -r1.1.1.2 -r1.1.1.2.2.1
--- fs/ext3/xattr.c 13 Sep 2006 17:45:05 -0000 1.1.1.2
+++ fs/ext3/xattr.c 13 Sep 2006 18:15:43 -0000 1.1.1.2.2.1
@@ -1008,6 +1008,7 @@ ext3_xattr_set_handle(handle_t *handle,
if (!error) {
ext3_xattr_update_super_block(handle, inode->i_sb);
inode->i_ctime = CURRENT_TIME_SEC;
+ inode->i_change_attribute++;
error = ext3_mark_iloc_dirty(handle, inode, &is.iloc);
/*
* The bh is consumed by ext3_mark_iloc_dirty, even with
Index: include/asm-i386/stat.h
===================================================================
RCS file: /home/ratchova/cvs/linux/include/asm-i386/stat.h,v
retrieving revision 1.1.1.2
retrieving revision 1.1.1.2.6.1
diff -u -p -r1.1.1.2 -r1.1.1.2.6.1
--- include/asm-i386/stat.h 28 Jun 2006 18:42:09 -0000 1.1.1.2
+++ include/asm-i386/stat.h 13 Sep 2006 18:15:43 -0000 1.1.1.2.6.1
@@ -32,7 +32,7 @@ struct stat {
unsigned long st_mtime_nsec;
unsigned long st_ctime;
unsigned long st_ctime_nsec;
- unsigned long __unused4;
+ unsigned long st_change_attribute;
unsigned long __unused5;
};

@@ -41,7 +41,7 @@ struct stat {
*/
struct stat64 {
unsigned long long st_dev;
- unsigned char __pad0[4];
+ unsigned long st_change_attribute;

#define STAT64_HAS_BROKEN_ST_INO 1
unsigned long __st_ino;
Index: include/asm-x86_64/stat.h
===================================================================
RCS file: /home/ratchova/cvs/linux/include/asm-x86_64/stat.h,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.10.1
diff -u -p -r1.1.1.1 -r1.1.1.1.10.1
--- include/asm-x86_64/stat.h 28 Jun 2006 18:41:22 -0000 1.1.1.1
+++ include/asm-x86_64/stat.h 13 Sep 2006 18:15:43 -0000 1.1.1.1.10.1
@@ -23,7 +23,8 @@ struct stat {
unsigned long st_mtime_nsec;
unsigned long st_ctime;
unsigned long st_ctime_nsec;
- long __unused[3];
+ unsigned long st_change_attribute;
+ long __unused[2];
};

/* For 32bit emulation */
Index: include/linux/ext3_fs.h
===================================================================
RCS file: /home/ratchova/cvs/linux/include/linux/ext3_fs.h,v
retrieving revision 1.1.1.3
retrieving revision 1.1.1.3.2.1
diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
--- include/linux/ext3_fs.h 13 Sep 2006 17:45:20 -0000 1.1.1.3
+++ include/linux/ext3_fs.h 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
@@ -286,7 +286,7 @@ struct ext3_inode {
__u16 i_pad1;
__le16 l_i_uid_high; /* these 2 fields */
__le16 l_i_gid_high; /* were reserved2[0] */
- __u32 l_i_reserved2;
+ __le32 l_i_change_attribute;
} linux2;
struct {
__u8 h_i_frag; /* Fragment number */
@@ -317,7 +317,7 @@ struct ext3_inode {
#define i_gid_low i_gid
#define i_uid_high osd2.linux2.l_i_uid_high
#define i_gid_high osd2.linux2.l_i_gid_high
-#define i_reserved2 osd2.linux2.l_i_reserved2
+#define i_chattr osd2.linux2.l_i_change_attribute

#elif defined(__GNU__)

Index: include/linux/fs.h
===================================================================
RCS file: /home/ratchova/cvs/linux/include/linux/fs.h,v
retrieving revision 1.1.1.3
retrieving revision 1.1.1.3.2.1
diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
--- include/linux/fs.h 13 Sep 2006 17:45:20 -0000 1.1.1.3
+++ include/linux/fs.h 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
@@ -280,6 +280,7 @@ typedef void (dio_iodone_t)(struct kiocb
#define ATTR_KILL_SUID 2048
#define ATTR_KILL_SGID 4096
#define ATTR_FILE 8192
+#define ATTR_CHANGE_ATTRIBUTE 16384

/*
* This is the Inode Attributes structure, used for notify_change(). It
@@ -299,6 +300,7 @@ struct iattr {
struct timespec ia_atime;
struct timespec ia_mtime;
struct timespec ia_ctime;
+ unsigned long ia_change_attribute;

/*
* Not an attribute, but an auxilary info for filesystems wanting to
@@ -510,6 +512,7 @@ struct inode {
struct timespec i_atime;
struct timespec i_mtime;
struct timespec i_ctime;
+ unsigned long i_change_attribute;
unsigned int i_blkbits;
unsigned long i_blksize;
unsigned long i_version;
Index: include/linux/stat.h
===================================================================
RCS file: /home/ratchova/cvs/linux/include/linux/stat.h,v
retrieving revision 1.1.1.2
retrieving revision 1.1.1.2.6.1
diff -u -p -r1.1.1.2 -r1.1.1.2.6.1
--- include/linux/stat.h 28 Jun 2006 18:42:11 -0000 1.1.1.2
+++ include/linux/stat.h 13 Sep 2006 18:15:43 -0000 1.1.1.2.6.1
@@ -68,6 +68,7 @@ struct kstat {
struct timespec atime;
struct timespec mtime;
struct timespec ctime;
+ unsigned long change_attribute;
unsigned long blksize;
unsigned long long blocks;
};


2006-09-13 18:11:11

by Trond Myklebust

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Wed, 2006-09-13 at 18:42 +0200, Alexandre Ratchov wrote:
> hello,
>
> here is a small patch that adds the "change attribute" for ext3
> file-systems;
>
> the change attribute is a simple counter that is reset to zero on
> inode creation and that is incremented every time the inode data is
> modified (similarly to the "ctime" time-stamp).

I would really have preferred a full-blown 64-bit counter as per
RFC3530, but I suppose we could always combine this change attribute
with the high word from ctime in order to make up the NFSv4 change
attribute. That should keep us safe until someone develops a ramdisk
with < 1 nsecond access time.

Cheers,
Trond

2006-09-13 18:30:25

by Alexandre Ratchov

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Wed, Sep 13, 2006 at 02:11:11PM -0400, Trond Myklebust wrote:
> On Wed, 2006-09-13 at 18:42 +0200, Alexandre Ratchov wrote:
> > hello,
> >
> > here is a small patch that adds the "change attribute" for ext3
> > file-systems;
> >
> > the change attribute is a simple counter that is reset to zero on
> > inode creation and that is incremented every time the inode data is
> > modified (similarly to the "ctime" time-stamp).
>
> I would really have preferred a full-blown 64-bit counter as per
> RFC3530, but I suppose we could always combine this change attribute
> with the high word from ctime in order to make up the NFSv4 change
> attribute. That should keep us safe until someone develops a ramdisk
> with < 1 nsecond access time.
>

do you mean something like "(ctime.tv_sec << 32) | change_attribute"? this
would allow 2^32 inode changes per second.

For ext3 it's hard to find unused bits in the on-disk inode structure, but
ext4 inode may become larger in the future, allowing a 64bit counter.

-- Alexandre

2006-09-13 19:06:13

by Trond Myklebust

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Wed, 2006-09-13 at 20:30 +0200, Alexandre Ratchov wrote:
> On Wed, Sep 13, 2006 at 02:11:11PM -0400, Trond Myklebust wrote:
> > On Wed, 2006-09-13 at 18:42 +0200, Alexandre Ratchov wrote:
> > > hello,
> > >
> > > here is a small patch that adds the "change attribute" for ext3
> > > file-systems;
> > >
> > > the change attribute is a simple counter that is reset to zero on
> > > inode creation and that is incremented every time the inode data is
> > > modified (similarly to the "ctime" time-stamp).
> >
> > I would really have preferred a full-blown 64-bit counter as per
> > RFC3530, but I suppose we could always combine this change attribute
> > with the high word from ctime in order to make up the NFSv4 change
> > attribute. That should keep us safe until someone develops a ramdisk
> > with < 1 nsecond access time.
> >
>
> do you mean something like "(ctime.tv_sec << 32) | change_attribute"? this
> would allow 2^32 inode changes per second.

Yes. As I said, that probably ought to suffice for now.

> For ext3 it's hard to find unused bits in the on-disk inode structure, but
> ext4 inode may become larger in the future, allowing a 64bit counter.

In anticipation of that event, could you please change the field in
'struct stat' and 'struct stat64' to be an 'unsigned long long' instead
of the current 'unsigned long'?

All the other fields are internal to the kernel, so a future change of
their size should not matter.

Cheers,
Trond


2006-09-13 19:31:38

by Andreas Dilger

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Sep 13, 2006 18:42 +0200, Alexandre Ratchov wrote:
> the change attribute is a simple counter that is reset to zero on
> inode creation and that is incremented every time the inode data is
> modified (similarly to the "ctime" time-stamp).

To start, I'm supportive of this concept, my comments are only to
get the most efficient implementation.

This appears to be very similar to the i_version field that is already
in the inode (which is also modified only by ext3), so instead of
increasing the size of the inode further we could use that field and
just make it persistent on disk. The i_version field is incremented
in mkdir/rmdir/create/rename. For Lustre it would also be desirable
to modify the version field for regular files when they are modified
(e.g. setattr, write), and it appears NFS v4 also wants the same
(assumed from use of file_update_time()). The question is whether
this should be handled internal to the filesystem (as ext3 does now)
or if it should be part of the VFS.

> The patch also adds a new ``st_change_attribute'' field in the stat
> structure, and modifies the stat(2) syscall accordingly. Currently the
> change is only visible on i386 and x86_64 archs.

Is this really necessary for knfsd?

> @@ -511,6 +511,7 @@ static struct inode *bm_get_inode(struct
> inode->i_blocks = 0;
> inode->i_atime = inode->i_mtime = inode->i_ctime =
> current_fs_time(inode->i_sb);
> + inode->i_change_attribute = 0;

Initializing to zero is more dangerous than any non-zero number,
since this is the most likely outcome of corruption... The current
ext3 code initializes i_version to a random number, and we can use
comparisons similar to jiffies as long as we don't expect > 2^31
changes between comparisons.

> +++ fs/inode.c 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
> @@ -1232,16 +1232,10 @@ void file_update_time(struct file *file)
> return;
>
> now = current_fs_time(inode->i_sb);
> - if (!timespec_equal(&inode->i_mtime, &now))
> - sync_it = 1;
> inode->i_mtime = now;
> -
> - if (!timespec_equal(&inode->i_ctime, &now))
> - sync_it = 1;
> inode->i_ctime = now;
> -
> - if (sync_it)
> - mark_inode_dirty_sync(inode);
> + inode->i_change_attribute++;
> + mark_inode_dirty_sync(inode);

Ugh, this would definitely hurt performance, because ext3_dirty_inode()
packs-for-disk the whole inode each time. I believe Stephen had patches
at one time to do the inode packing at transaction commit time instead
of when it is changed, so we only do the packing once. Having a generic
mechanism to do pre-commit callbacks from jbd (i.e. to pack a dirty
inode to the buffer) would also be useful for other things like doing
the inode or group descriptor checksum only once per transaction...

> Index: include/linux/ext3_fs.h
> ===================================================================
> RCS file: /home/ratchova/cvs/linux/include/linux/ext3_fs.h,v
> retrieving revision 1.1.1.3
> retrieving revision 1.1.1.3.2.1
> diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
> --- include/linux/ext3_fs.h 13 Sep 2006 17:45:20 -0000 1.1.1.3
> +++ include/linux/ext3_fs.h 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
> @@ -286,7 +286,7 @@ struct ext3_inode {
> __u16 i_pad1;
> __le16 l_i_uid_high; /* these 2 fields */
> __le16 l_i_gid_high; /* were reserved2[0] */
> - __u32 l_i_reserved2;
> + __le32 l_i_change_attribute;

There was some other use of the reserved fields for ext4 64-bit-blocks
support. One was for i_file_acl_hi (I think this is using the i_pad1
above), one was for i_blocks_hi (I believe this was proposed to use the
i_frag and i_fsize bytes). Is this conflicting with anything else?
There were a lot of proposals for these fields, and I don't recall which
ones are still out there.

> @@ -280,6 +280,7 @@ typedef void (dio_iodone_t)(struct kiocb
> +#define ATTR_CHANGE_ATTRIBUTE 16384

Do you need a setattr interface for this, or is it sufficient to use
the i_version field from the inode, and let the filesystem manage
i_version updates as it is doing now?

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.


2006-09-13 20:30:25

by Randy Dunlap

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Wed, 13 Sep 2006 15:06:02 -0400 Trond Myklebust wrote:

> On Wed, 2006-09-13 at 20:30 +0200, Alexandre Ratchov wrote:
> > On Wed, Sep 13, 2006 at 02:11:11PM -0400, Trond Myklebust wrote:
> > > On Wed, 2006-09-13 at 18:42 +0200, Alexandre Ratchov wrote:
> > > > hello,
> > > >
> > > > here is a small patch that adds the "change attribute" for ext3
> > > > file-systems;
> > > >
> > > > the change attribute is a simple counter that is reset to zero on
> > > > inode creation and that is incremented every time the inode data is
> > > > modified (similarly to the "ctime" time-stamp).
> > >
> > > I would really have preferred a full-blown 64-bit counter as per
> > > RFC3530, but I suppose we could always combine this change attribute
> > > with the high word from ctime in order to make up the NFSv4 change
> > > attribute. That should keep us safe until someone develops a ramdisk
> > > with < 1 nsecond access time.
> > >
> >
> > do you mean something like "(ctime.tv_sec << 32) | change_attribute"? this
> > would allow 2^32 inode changes per second.
>
> Yes. As I said, that probably ought to suffice for now.
>
> > For ext3 it's hard to find unused bits in the on-disk inode structure, but
> > ext4 inode may become larger in the future, allowing a 64bit counter.
>
> In anticipation of that event, could you please change the field in
> 'struct stat' and 'struct stat64' to be an 'unsigned long long' instead
> of the current 'unsigned long'?
>
> All the other fields are internal to the kernel, so a future change of
> their size should not matter.

and while you are making changes + resubmitting,
Signed-off-by:
needs to have name + email address.

---
~Randy

2006-09-14 01:24:52

by J. Bruce Fields

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Wed, Sep 13, 2006 at 01:31:30PM -0600, Andreas Dilger wrote:
> On Sep 13, 2006 18:42 +0200, Alexandre Ratchov wrote:
> > The patch also adds a new ``st_change_attribute'' field in the stat
> > structure, and modifies the stat(2) syscall accordingly. Currently the
> > change is only visible on i386 and x86_64 archs.
>
> Is this really necessary for knfsd?

Of course knfsd is completely in kernel, so it doesn't care about the
userspace interface.

But I think that a change attribute is potentially an *extremely* useful
thing, and for more than just nfs servers. Lots of userspace programs
also need to know whether a file has changed since they last examined
it, and also suffer from the limitations of using ctime or mtime as an
imperfect approximation to a real change attribute.

But it would make sense to split the user space changes into a second
patch and possibly apply it later.

--b.

2006-09-14 09:23:26

by Andreas Dilger

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Sep 13, 2006 20:30 +0200, Alexandre Ratchov wrote:
> On Wed, Sep 13, 2006 at 02:11:11PM -0400, Trond Myklebust wrote:
> > On Wed, 2006-09-13 at 18:42 +0200, Alexandre Ratchov wrote:
> > > the change attribute is a simple counter that is reset to zero on
> > > inode creation and that is incremented every time the inode data is
> > > modified (similarly to the "ctime" time-stamp).
> >
> > I would really have preferred a full-blown 64-bit counter as per
> > RFC3530, but I suppose we could always combine this change attribute
> > with the high word from ctime in order to make up the NFSv4 change
> > attribute. That should keep us safe until someone develops a ramdisk
> > with < 1 nsecond access time.
>
> do you mean something like "(ctime.tv_sec << 32) | change_attribute"? this
> would allow 2^32 inode changes per second.

It might be preferrable, since we are depending on the ctime here anyways,
is to combine this with the nsec-resolution ctime, and kill two birds with
one field in the inode.

The implementation would be to update the ctime+nsec field as normal, but
in the unlikely case that both the second+nsec ctime is the same as before
the nsec value would be incremented by 1. This could happen in case of
low-resolution kernel timers, and would also handle the future case where
the inode is modified more than once in the same nanosecond.

The other benefit is that it allows comparisons between two different
inodes to be more meaningful, instead of just using the seconds + random
version number.

It would be possible/desirable to make the nsec ctime field be part of the
small inode (using the proposed reserved field) instead of the large inode,
since that is a requirement for working with existing ext3 filesystems. The
previous nsec timestamp patch would only need trivial modifications to make
this work, just #define i_ctime_extra to be l_i_reserved1 I believe.


Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.


2006-09-14 13:22:04

by Alexandre Ratchov

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Wed, Sep 13, 2006 at 01:31:30PM -0600, Andreas Dilger wrote:
> On Sep 13, 2006 18:42 +0200, Alexandre Ratchov wrote:
> > the change attribute is a simple counter that is reset to zero on
> > inode creation and that is incremented every time the inode data is
> > modified (similarly to the "ctime" time-stamp).
>
> To start, I'm supportive of this concept, my comments are only to
> get the most efficient implementation.
>
> This appears to be very similar to the i_version field that is already
> in the inode (which is also modified only by ext3), so instead of
> increasing the size of the inode further we could use that field and
> just make it persistent on disk. The i_version field is incremented
> in mkdir/rmdir/create/rename. For Lustre it would also be desirable
> to modify the version field for regular files when they are modified
> (e.g. setattr, write), and it appears NFS v4 also wants the same
> (assumed from use of file_update_time()). The question is whether
> this should be handled internal to the filesystem (as ext3 does now)
> or if it should be part of the VFS.

hmm..., i_version is currently used for directory entries validation; i've
browsed the ext{2,3,4} sources and i don't see any drawbacks in merging
i_version and i_chattr.

IMHO, the natural place to do this stuff is the VFS, because it can be
common to all file-systems supporting this feature. Currently it's the same
with ctime, mtime and atime. These are in the VFS even if there are
file-systems that don't support all of them.

> > The patch also adds a new ``st_change_attribute'' field in the stat
> > structure, and modifies the stat(2) syscall accordingly. Currently the
> > change is only visible on i386 and x86_64 archs.
>
> Is this really necessary for knfsd?
>
> > @@ -511,6 +511,7 @@ static struct inode *bm_get_inode(struct
> > inode->i_blocks = 0;
> > inode->i_atime = inode->i_mtime = inode->i_ctime =
> > current_fs_time(inode->i_sb);
> > + inode->i_change_attribute = 0;
>
> Initializing to zero is more dangerous than any non-zero number,
> since this is the most likely outcome of corruption... The current
> ext3 code initializes i_version to a random number, and we can use
> comparisons similar to jiffies as long as we don't expect > 2^31
> changes between comparisons.
>

it's ok for me;

> > +++ fs/inode.c 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
> > @@ -1232,16 +1232,10 @@ void file_update_time(struct file *file)
> > return;
> >
> > now = current_fs_time(inode->i_sb);
> > - if (!timespec_equal(&inode->i_mtime, &now))
> > - sync_it = 1;
> > inode->i_mtime = now;
> > -
> > - if (!timespec_equal(&inode->i_ctime, &now))
> > - sync_it = 1;
> > inode->i_ctime = now;
> > -
> > - if (sync_it)
> > - mark_inode_dirty_sync(inode);
> > + inode->i_change_attribute++;
> > + mark_inode_dirty_sync(inode);
>
> Ugh, this would definitely hurt performance, because ext3_dirty_inode()
> packs-for-disk the whole inode each time. I believe Stephen had patches
> at one time to do the inode packing at transaction commit time instead
> of when it is changed, so we only do the packing once. Having a generic
> mechanism to do pre-commit callbacks from jbd (i.e. to pack a dirty
> inode to the buffer) would also be useful for other things like doing
> the inode or group descriptor checksum only once per transaction...
>

yes, that part is ugly. I've thought about another solution, but i don't
know if this would work:

afaik, for an open file, there is always a copy of the inode in memory,
because there is a reference to it in the file structure. So, in principle,
we shouldn't need to make dirty the inode. I don't know if this is feasable
perhaps am i missing something here.

> > Index: include/linux/ext3_fs.h
> > ===================================================================
> > RCS file: /home/ratchova/cvs/linux/include/linux/ext3_fs.h,v
> > retrieving revision 1.1.1.3
> > retrieving revision 1.1.1.3.2.1
> > diff -u -p -r1.1.1.3 -r1.1.1.3.2.1
> > --- include/linux/ext3_fs.h 13 Sep 2006 17:45:20 -0000 1.1.1.3
> > +++ include/linux/ext3_fs.h 13 Sep 2006 18:15:43 -0000 1.1.1.3.2.1
> > @@ -286,7 +286,7 @@ struct ext3_inode {
> > __u16 i_pad1;
> > __le16 l_i_uid_high; /* these 2 fields */
> > __le16 l_i_gid_high; /* were reserved2[0] */
> > - __u32 l_i_reserved2;
> > + __le32 l_i_change_attribute;
>
> There was some other use of the reserved fields for ext4 64-bit-blocks
> support. One was for i_file_acl_hi (I think this is using the i_pad1
> above), one was for i_blocks_hi (I believe this was proposed to use the
> i_frag and i_fsize bytes). Is this conflicting with anything else?
> There were a lot of proposals for these fields, and I don't recall which
> ones are still out there.

i haven't noticed any conflicts here.

>
> > @@ -280,6 +280,7 @@ typedef void (dio_iodone_t)(struct kiocb
> > +#define ATTR_CHANGE_ATTRIBUTE 16384
>
> Do you need a setattr interface for this, or is it sufficient to use
> the i_version field from the inode, and let the filesystem manage
> i_version updates as it is doing now?

it's not strictly necessary; it's not more necessary that the interface to
ctime or other attributes. It's here for completness, in my opinion the
change attribute is the same as the ctime time-stamp

thanks for your comments

-- Alexandre

2006-09-14 13:46:03

by Peter Staubach

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

Trond Myklebust wrote:
> On Wed, 2006-09-13 at 18:42 +0200, Alexandre Ratchov wrote:
>
>> hello,
>>
>> here is a small patch that adds the "change attribute" for ext3
>> file-systems;
>>
>> the change attribute is a simple counter that is reset to zero on
>> inode creation and that is incremented every time the inode data is
>> modified (similarly to the "ctime" time-stamp).
>>
>
> I would really have preferred a full-blown 64-bit counter as per
> RFC3530, but I suppose we could always combine this change attribute
> with the high word from ctime in order to make up the NFSv4 change
> attribute. That should keep us safe until someone develops a ramdisk
> with < 1 nsecond access time.

Wouldn't the generation count work better than ctime to differentiate
between
instances of files using the same inode number? That way, there wouldn't be
a clock resolution issue.

Thanx...

ps

2006-09-14 13:48:31

by Alexandre Ratchov

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Thu, Sep 14, 2006 at 03:23:18AM -0600, Andreas Dilger wrote:
> On Sep 13, 2006 20:30 +0200, Alexandre Ratchov wrote:
> > On Wed, Sep 13, 2006 at 02:11:11PM -0400, Trond Myklebust wrote:
> > > On Wed, 2006-09-13 at 18:42 +0200, Alexandre Ratchov wrote:
> > > > the change attribute is a simple counter that is reset to zero on
> > > > inode creation and that is incremented every time the inode data is
> > > > modified (similarly to the "ctime" time-stamp).
> > >
> > > I would really have preferred a full-blown 64-bit counter as per
> > > RFC3530, but I suppose we could always combine this change attribute
> > > with the high word from ctime in order to make up the NFSv4 change
> > > attribute. That should keep us safe until someone develops a ramdisk
> > > with < 1 nsecond access time.
> >
> > do you mean something like "(ctime.tv_sec << 32) | change_attribute"? this
> > would allow 2^32 inode changes per second.
>
> It might be preferrable, since we are depending on the ctime here anyways,
> is to combine this with the nsec-resolution ctime, and kill two birds with
> one field in the inode.
>
> The implementation would be to update the ctime+nsec field as normal, but
> in the unlikely case that both the second+nsec ctime is the same as before
> the nsec value would be incremented by 1. This could happen in case of
> low-resolution kernel timers, and would also handle the future case where
> the inode is modified more than once in the same nanosecond.
>
> The other benefit is that it allows comparisons between two different
> inodes to be more meaningful, instead of just using the seconds + random
> version number.
>
> It would be possible/desirable to make the nsec ctime field be part of the
> small inode (using the proposed reserved field) instead of the large inode,
> since that is a requirement for working with existing ext3 filesystems. The
> previous nsec timestamp patch would only need trivial modifications to make
> this work, just #define i_ctime_extra to be l_i_reserved1 I believe.
>

there is something i dislike with incrementing the nsec value. The ctime is
a global (as opposed to per-inode) time reference for the file-system. And
it is expected to be globally coherent; imagine the following situation:

Within the same time-slice (with time-stamp T0, in nanoseconds), we do the
following in this order:

change file1 -> ctime = T0
change file2 -> ctime = T0
change file2 -> ctime = T0 + 1
change file2 -> ctime = T0 + 2
change file1 -> ctime = T0 + 1

so it appears that file2 is strictly newer than file1, which is false. So
the assumption "if ctime(file1) < ctime(file2) then file2 is newer that
file1" is no longer true.

In order to fix this, we'll need to increment a global counter, not a
pre-inode counter. It's feasable.

cheers,

-- Alexandre

2006-09-14 13:56:20

by Trond Myklebust

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Thu, 2006-09-14 at 09:46 -0400, Peter Staubach wrote:
> Wouldn't the generation count work better than ctime to differentiate
> between
> instances of files using the same inode number? That way, there wouldn't be
> a clock resolution issue.

No. This is about distinguishing updates to the metadata/data of the
same instance. It is exactly what ctime was supposed to do, but ctime
relies on a clock which usually has too poor time resolution, may not be
monotonic, etc...

Cheers,
Trond

2006-09-14 14:06:48

by Alexandre Ratchov

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Thu, Sep 14, 2006 at 09:46:03AM -0400, Peter Staubach wrote:
> Trond Myklebust wrote:
> >On Wed, 2006-09-13 at 18:42 +0200, Alexandre Ratchov wrote:
> >
> >>hello,
> >>
> >>here is a small patch that adds the "change attribute" for ext3
> >>file-systems;
> >>
> >>the change attribute is a simple counter that is reset to zero on
> >>inode creation and that is incremented every time the inode data is
> >>modified (similarly to the "ctime" time-stamp).
> >>
> >
> >I would really have preferred a full-blown 64-bit counter as per
> >RFC3530, but I suppose we could always combine this change attribute
> >with the high word from ctime in order to make up the NFSv4 change
> >attribute. That should keep us safe until someone develops a ramdisk
> >with < 1 nsecond access time.
>
> Wouldn't the generation count work better than ctime to differentiate
> between
> instances of files using the same inode number? That way, there wouldn't be
> a clock resolution issue.

Yes, and afaik it's already used for that purpose by NFSv{2,3}.

Note that the change attribute is for counting changes of the same instance
of a file using a given inode (as opposed to the generation counter that's
used to count the number of files that have used a given inode).

-- Alexandre

2006-09-14 21:02:00

by Andreas Dilger

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Sep 14, 2006 15:21 +0200, Alexandre Ratchov wrote:
> IMHO, the natural place to do this stuff is the VFS, because it can be
> common to all file-systems supporting this feature. Currently it's the same
> with ctime, mtime and atime. These are in the VFS even if there are
> file-systems that don't support all of them.

Well, that is only partly true. I see lots of places in ext3 that are
setting i_ctime and i_mtime...

> > Ugh, this would definitely hurt performance, because ext3_dirty_inode()
> > packs-for-disk the whole inode each time. I believe Stephen had patches
> > at one time to do the inode packing at transaction commit time instead
> > of when it is changed, so we only do the packing once. Having a generic
> > mechanism to do pre-commit callbacks from jbd (i.e. to pack a dirty
> > inode to the buffer) would also be useful for other things like doing
> > the inode or group descriptor checksum only once per transaction...
>
> afaik, for an open file, there is always a copy of the inode in memory,
> because there is a reference to it in the file structure. So, in principle,
> we shouldn't need to make dirty the inode. I don't know if this is feasable
> perhaps am i missing something here.

The in-memory inode needs to be copied into the buffer so that it is
part of the transaction being committed to disk, or updates are lost.
This was a common bug with early ext3 - marking the inode dirty and
then changing a field in the in-core inode - which would not be saved
to disk. In other filesystems this is only a few-cycle race, but in
ext3 the in-core inode is not written to disk unless the inode is
again marked dirty.

The potential benefit of making this a callback from the JBD layer is
it avoids copying the inode for EVERY dirty, and only doing it once
per transaction. Add a list of callbacks hooked onto the transaction
to be called before it is committed, and the callback data is the
inode pointer which does a single ext3_do_update_inode() call if the
inode is still marked dirty.

> it's not strictly necessary; it's not more necessary that the interface to
> ctime or other attributes. It's here for completness, in my opinion the
> change attribute is the same as the ctime time-stamp

Then makes sense to just improve the ctime mechanism instead of adding
new code and interfaces...

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.


2006-09-15 10:19:40

by Alexandre Ratchov

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Thu, Sep 14, 2006 at 03:01:48PM -0600, Andreas Dilger wrote:
> On Sep 14, 2006 15:21 +0200, Alexandre Ratchov wrote:
> > IMHO, the natural place to do this stuff is the VFS, because it can be
> > common to all file-systems supporting this feature. Currently it's the same
> > with ctime, mtime and atime. These are in the VFS even if there are
> > file-systems that don't support all of them.
>
> Well, that is only partly true. I see lots of places in ext3 that are
> setting i_ctime and i_mtime...
>

i fully agree here; personnally in the long term, i'd like to see all this
stuff common to all file systems. IMHO, the file-system specific code should
only handle the on-disk storage part of the problem.

> > > Ugh, this would definitely hurt performance, because ext3_dirty_inode()
> > > packs-for-disk the whole inode each time. I believe Stephen had patches
> > > at one time to do the inode packing at transaction commit time instead
> > > of when it is changed, so we only do the packing once. Having a generic
> > > mechanism to do pre-commit callbacks from jbd (i.e. to pack a dirty
> > > inode to the buffer) would also be useful for other things like doing
> > > the inode or group descriptor checksum only once per transaction...
> >
> > afaik, for an open file, there is always a copy of the inode in memory,
> > because there is a reference to it in the file structure. So, in principle,
> > we shouldn't need to make dirty the inode. I don't know if this is feasable
> > perhaps am i missing something here.
>
> The in-memory inode needs to be copied into the buffer so that it is
> part of the transaction being committed to disk, or updates are lost.
> This was a common bug with early ext3 - marking the inode dirty and
> then changing a field in the in-core inode - which would not be saved
> to disk. In other filesystems this is only a few-cycle race, but in
> ext3 the in-core inode is not written to disk unless the inode is
> again marked dirty.
>
> The potential benefit of making this a callback from the JBD layer is
> it avoids copying the inode for EVERY dirty, and only doing it once
> per transaction. Add a list of callbacks hooked onto the transaction
> to be called before it is committed, and the callback data is the
> inode pointer which does a single ext3_do_update_inode() call if the
> inode is still marked dirty.
>

yes, that would be a real solution. Do you have any reference to Stephen
patches?

BTW, note that when we'll add support for nanosecond time-stamps, we still
have the same problem, because with a very high clock and time-stamp
resolutions, we'll have to update the inode on every change.

> > it's not strictly necessary; it's not more necessary that the interface to
> > ctime or other attributes. It's here for completness, in my opinion the
> > change attribute is the same as the ctime time-stamp
>
> Then makes sense to just improve the ctime mechanism instead of adding
> new code and interfaces...
>

that's also my opinion, and i really see the change attribute as part of the
ctime mechanism. It adds very few lines code (most of them are trivial) and
it uses the same interface as ctime.

The problem we want to solve is how to track relaiably file changes; there
are several solutions. The solution that Celine and me have considered is
the change attribute. It uses the 'ctime' semantics so we used the ctime
interface.

It's available for kernel threads, but I don't see any reason not to make it
available for user-land programs. In order to use it, user-land will just
need some trivial modifications.

Note that the change attribute is not incompatible with nanosecond
time-stamps. Its just a complement to the ctime, regardless of the time
resolution.

-- Alexandre


2006-09-15 16:18:23

by Andreas Dilger

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Sep 15, 2006 12:19 +0200, Alexandre Ratchov wrote:
> BTW, note that when we'll add support for nanosecond time-stamps, we still
> have the same problem, because with a very high clock and time-stamp
> resolutions, we'll have to update the inode on every change.

If we have a journal commit callback, then even if the in-core inode ctime
is changed continually, the on-disk inode will be updated exactly once per
jbd transaction. The problem as it stands now is that the ext3/VFS code
doesn't know where a transaction boundary is, so it has to continually
update the on-disk inode to make sure that the changes are in the relevant
transaction.

I recall a long time ago that Andrew got significant improvement from
reducing the number of mark_inode_dirty() calls in ext3, just because of
avoiding the repeated core->disk inode packing.

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.


2006-11-14 22:17:25

by Andreas Dilger

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Sep 13, 2006 20:30 +0200, Alexandre Ratchov wrote:
> On Wed, Sep 13, 2006 at 02:11:11PM -0400, Trond Myklebust wrote:
> > I would really have preferred a full-blown 64-bit counter as per
> > RFC3530, but I suppose we could always combine this change attribute
> > with the high word from ctime in order to make up the NFSv4 change
> > attribute. That should keep us safe until someone develops a ramdisk
> > with < 1 nsecond access time.
>
> do you mean something like "(ctime.tv_sec << 32) | change_attribute"? this
> would allow 2^32 inode changes per second.

I've been giving this further thought, and it may be that a full 64-bit
counter per inode is the only bulletproof solution.

One reason that ctime+nsec as the version number isn't so great is that if
there is some reason to set the clock backward (i.e. it was incorrectly
set into the future at some point) the inode ctime may jump backward.
This could cause either misordering of events, or collisions between
version numbers. The problem could be mitigated by having the ctime+nsec
value only increment the nsec component by 1 for each new version (like
a counter) until real time catches up with the bad ctime, but it might
leave files with a bad ctime for a long time.

Other than not being able to set ctime backward (which isn't really
something that should happen under normal behaviour), this is a reasonable
solution.

The main drawback of a 64-bit counter is the space in the inode that it
consumes... I don't think we can find 64 bits of free space in the core
inode, so this would relegate the solution to new filesystems that are
formatted with large inodes.

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.

2006-11-24 00:23:11

by Andreas Dilger

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Nov 14, 2006 15:17 -0700, Andreas Dilger wrote:
> On Sep 13, 2006 20:30 +0200, Alexandre Ratchov wrote:
> > On Wed, Sep 13, 2006 at 02:11:11PM -0400, Trond Myklebust wrote:
> > > I would really have preferred a full-blown 64-bit counter as per
> > > RFC3530, but I suppose we could always combine this change attribute
> > > with the high word from ctime in order to make up the NFSv4 change
> > > attribute. That should keep us safe until someone develops a ramdisk
> > > with < 1 nsecond access time.
> >
> > do you mean something like "(ctime.tv_sec << 32) | change_attribute"? this
> > would allow 2^32 inode changes per second.
>
> I've been giving this further thought, and it may be that a full 64-bit
> counter per inode is the only bulletproof solution.
>
> One reason that ctime+nsec as the version number isn't so great is that if
> there is some reason to set the clock backward (i.e. it was incorrectly
> set into the future at some point) the inode ctime may jump backward.
> This could cause either misordering of events, or collisions between
> version numbers. The problem could be mitigated by having the ctime+nsec
> value only increment the nsec component by 1 for each new version (like
> a counter) until real time catches up with the bad ctime, but it might
> leave files with a bad ctime for a long time.
>
> The main drawback of a 64-bit counter is the space in the inode that it
> consumes... I don't think we can find 64 bits of free space in the core
> inode, so this would relegate the solution to new filesystems that are
> formatted with large inodes.

Alexandre, Trond,
what do you think about using a 32-bit in-inode version (sufficient for
causal uses of NFSv4), and put the 32-bit MSB of the version into the
large part of the inode (say after cr_time)?

That allows use of the version for existing ext3 filesystems, and with
large inodes (Lustre, ext4) it also meets the specs of RFC 3530 and any
intended NFSv4 future use?

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.

2006-11-28 19:00:16

by J. Bruce Fields

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Thu, Nov 23, 2006 at 05:23:11PM -0700, Andreas Dilger wrote:
> On Nov 14, 2006 15:17 -0700, Andreas Dilger wrote:
> > I've been giving this further thought, and it may be that a full 64-bit
> > counter per inode is the only bulletproof solution.
> >
> > One reason that ctime+nsec as the version number isn't so great is that if
> > there is some reason to set the clock backward (i.e. it was incorrectly
> > set into the future at some point) the inode ctime may jump backward.
> > This could cause either misordering of events, or collisions between
> > version numbers. The problem could be mitigated by having the ctime+nsec
> > value only increment the nsec component by 1 for each new version (like
> > a counter) until real time catches up with the bad ctime, but it might
> > leave files with a bad ctime for a long time.
> >
> > The main drawback of a 64-bit counter is the space in the inode that it
> > consumes... I don't think we can find 64 bits of free space in the core
> > inode, so this would relegate the solution to new filesystems that are
> > formatted with large inodes.
>
> Alexandre, Trond,
> what do you think about using a 32-bit in-inode version (sufficient for
> causal uses of NFSv4),
> and put the 32-bit MSB of the version into the
> large part of the inode (say after cr_time)?

So does that mean that the MSB of the change attribute would only be
available on some filesystems, or that it would be available on all of
them but be slower on those with smaller inodes? And how does the user
(e.g. the nfsd code) distinguish the two cases?

> That allows use of the version for existing ext3 filesystems, and with
> large inodes (Lustre, ext4) it also meets the specs of RFC 3530 and any
> intended NFSv4 future use?

--b.

2006-11-28 22:06:22

by Andreas Dilger

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Nov 28, 2006 14:00 -0500, J. Bruce Fields wrote:
> On Thu, Nov 23, 2006 at 05:23:11PM -0700, Andreas Dilger wrote:
> > On Nov 14, 2006 15:17 -0700, Andreas Dilger wrote:
> > > I've been giving this further thought, and it may be that a full 64-bit
> > > counter per inode is the only bulletproof solution.
> > >
> > > One reason that ctime+nsec as the version number isn't so great is that if
> > > there is some reason to set the clock backward (i.e. it was incorrectly
> > > set into the future at some point) the inode ctime may jump backward.
> > > This could cause either misordering of events, or collisions between
> > > version numbers. The problem could be mitigated by having the ctime+nsec
> > > value only increment the nsec component by 1 for each new version (like
> > > a counter) until real time catches up with the bad ctime, but it might
> > > leave files with a bad ctime for a long time.
> > >
> > > The main drawback of a 64-bit counter is the space in the inode that it
> > > consumes... I don't think we can find 64 bits of free space in the core
> > > inode, so this would relegate the solution to new filesystems that are
> > > formatted with large inodes.
> >
> > Alexandre, Trond,
> > what do you think about using a 32-bit in-inode version (sufficient for
> > causal uses of NFSv4),
> > and put the 32-bit MSB of the version into the
> > large part of the inode (say after cr_time)?
>
> So does that mean that the MSB of the change attribute would only be
> available on some filesystems, or that it would be available on all of
> them but be slower on those with smaller inodes? And how does the user
> (e.g. the nfsd code) distinguish the two cases?

One other option is to use the other reserved field (l_i_reserved2) to
store the MSB of the version.

> > That allows use of the version for existing ext3 filesystems, and with
> > large inodes (Lustre, ext4) it also meets the specs of RFC 3530 and any
> > intended NFSv4 future use?

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.

2006-11-29 18:53:01

by Cordenner jean noel

[permalink] [raw]
Subject: [RFC] [patch 0/3] change attribute for ext4

Hello,

I've updated the change attribute patch for ext4 that was initially posted by
Alexandre Ratchov.

The change attribute is a simple counter that is set on inode creation and that
is incremented every time the inode data is modified (similarly to the "ctime"
time-stamp) and never reset.

Here are the results of tests I ran with and without the change attribute patch.

http://www.bullopensource.org/ext4/change_attribute/index.html


Any comments are welcome.

regards,
Jean noel

2006-12-14 01:24:31

by Andreas Dilger

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Sep 13, 2006 14:11 -0400, Trond Myklebust wrote:
> On Wed, 2006-09-13 at 18:42 +0200, Alexandre Ratchov wrote:
> > here is a small patch that adds the "change attribute" for ext3
> > file-systems;
> >
> > the change attribute is a simple counter that is reset to zero on
> > inode creation and that is incremented every time the inode data is
> > modified (similarly to the "ctime" time-stamp).
>
> I would really have preferred a full-blown 64-bit counter as per
> RFC3530, but I suppose we could always combine this change attribute
> with the high word from ctime in order to make up the NFSv4 change
> attribute. That should keep us safe until someone develops a ramdisk
> with < 1 nsecond access time.

Trond, can you please elaborate on the need for a 64-bit version counter
for NFSv4? We have been looking at something similar, but ctime+nsec is
not really sufficient as it is possible that the inode ctime can go
backward if the clock is reset.

What kind of requirements does NFSv4 place on the version? Monotonic is
probably a good bet. Does it need to be global for the filesystem, or
is a per-inode version sufficient? What functionality of NFSv4 needs
the version?

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.

2006-12-14 01:52:12

by J. Bruce Fields

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Wed, Dec 13, 2006 at 06:24:28PM -0700, Andreas Dilger wrote:
> On Sep 13, 2006 14:11 -0400, Trond Myklebust wrote:
> > I would really have preferred a full-blown 64-bit counter as per
> > RFC3530, but I suppose we could always combine this change attribute
> > with the high word from ctime in order to make up the NFSv4 change
> > attribute. That should keep us safe until someone develops a ramdisk
> > with < 1 nsecond access time.
>
> Trond, can you please elaborate on the need for a 64-bit version counter
> for NFSv4?

I'm not Trond, but....

> What kind of requirements does NFSv4 place on the version? Monotonic is
> probably a good bet.

The only requirement is that it be unique (assuming a file is never
modified 2^64 times). Clients can't compare them except for equality.

> Does it need to be global for the filesystem

Nope.

> or is a per-inode version sufficient?

Yes.

> What functionality of NFSv4 needs the version?

Clients use it to revalidate their caches.

--b.

2006-12-14 16:48:09

by Trond Myklebust

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Wed, 2006-12-13 at 20:52 -0500, J. Bruce Fields wrote:
> > What kind of requirements does NFSv4 place on the version? Monotonic is
> > probably a good bet.
>
> The only requirement is that it be unique (assuming a file is never
> modified 2^64 times). Clients can't compare them except for equality.

The other requirement is that they be updated in more or less any
situation where you would normally see a 'ctime' update. In other words
any time when the file metadata or data changes, and any time when the
ACL changes.

(NB: I'm not sure what we should do w.r.t. xattr changes since those are
not really covered by RFC3530.)

Atomicity is not a hard requirement, however the server is required to
know whether or not the update was atomic. If the update is atomic, a
careful client may perform certain optimisations based upon it knowing
that no other changes to the inode have raced with this one. For
instance, if it knows that a file creation atomically updated the change
attribute of the directory, then it can determine that it does not need
to check for other changes to that directory.

> > Does it need to be global for the filesystem
>
> Nope.
>
> > or is a per-inode version sufficient?
>
> Yes.

Yes. If your filesystem wants to support Solaris or Reiser4-like
subfiles, then it is expected that each subfile should have its own
change attribute (whereas changes to the subfile 'directory' will be
reflected by the parent inode's change attribute.

Change attribute values may be reused if the inode number is reused (as
long as the filesystem has something like a generation counter that
allows it to distinguish between different instances of the same inode
number).

> > What functionality of NFSv4 needs the version?
>
> Clients use it to revalidate their caches.

Yup. It is used to detect changes made on the NFS server itself
(possibly by other NFS clients, possibly by local processes on the
server), so that the client can flush out any stale cached data.

Cheers
Trond

2006-12-14 23:04:57

by Andreas Dilger

[permalink] [raw]
Subject: Re: rfc: [patch] change attribute for ext3

On Dec 14, 2006 11:48 -0500, Trond Myklebust wrote:
> > The only requirement is that it be unique (assuming a file is never
> > modified 2^64 times). Clients can't compare them except for equality.
>
> The other requirement is that they be updated in more or less any
> situation where you would normally see a 'ctime' update. In other words
> any time when the file metadata or data changes, and any time when the
> ACL changes.

So, to confirm - if existing ext3 filesystems only had 32 bits for the
change attribute, but ext4 filesystems had 64 bits, would that be OK?
If the change attribute would wrap in rare cases, it would still satisfy
the inequality check, but the sequential update behaviour would be lost
for that one update and the client may unnecessarily flush its cache,
or in extremely rare cases NOT flush its cache (if it was disconnected
for exactly 2^32 updates).

If there is a hard requirement for 64-bit change attributes then this
wouldn't be possible without forcing a reformat and update to ext4
with large inodes.

> Yup. It is used to detect changes made on the NFS server itself
> (possibly by other NFS clients, possibly by local processes on the
> server), so that the client can flush out any stale cached data.

What API do you think would be good for getting the change attribute?
I was thinking using i_version for this would be appropriate, since
that is used for the same function already (internal revalidation of
an inode after a lock was dropped temporarily). It would need to be
extended to 64 bits in size (if you can convince the rest of the
kernel developers about this) or alternately an export_operations method
and the filesystem stores the 64-bit version in its inode_info?

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.