Skip to contentSkip to conversation

read-only bind mount prep work

PATCH In review Dave Hansen 5 participants 21 messages Aug 9, 2006 – Aug 12, 2006

Subsystems (27)
Networking Memory management VFS USB +23 more subsystems
lore
Share & tools
lore
#0 lore raw

cover read-only bind mount prep work

The following patches lay some of the groundwork for the read-only bind mounts. Christoph Hellwig suggested that I send them in separately. This was a good suggestion, as the bulk of the modifications of the entire set happen in here, and these should make no functional changes, and should be pretty easy to merge.

-- Dave

#1 lore raw

PATCH 1/6 prepare for write access checks: collapse if()

We're shortly going to be adding a bunch more permission checks in these functions. That requires adding either a bunch of new if() conditions, or some gotos. This patch collapses existing if()s and uses gotos instead to prepare for the upcoming changes.

---

 lxc-dave/fs/namei.c |   93 +++++++++++++++++++++++++++-------------------------
 lxc-dave/fs/open.c  |   64 ++++++++++++++++++++---------------
 2 files changed, 87 insertions(+), 70 deletions(-)
fs/namei.c renamed from fs/namei.c~B1-prepwork-collapse-ifs +50 -43
diff -puN fs/namei.c~B1-prepwork-collapse-ifs fs/namei.c
@@ -1926,30 +1926,32 @@ asmlinkage long sys_mkdirat(int dfd, con
 {
 	int error = 0;
 	char * tmp;
+	struct dentry *dentry;
+	struct nameidata nd;
 
 	tmp = getname(pathname);
 	error = PTR_ERR(tmp);
-	if (!IS_ERR(tmp)) {
-		struct dentry *dentry;
-		struct nameidata nd;
+	if (IS_ERR(tmp))
+		goto out_err;
 
-		error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
-		if (error)
-			goto out;
-		dentry = lookup_create(&nd, 1);
-		error = PTR_ERR(dentry);
-		if (!IS_ERR(dentry)) {
-			if (!IS_POSIXACL(nd.dentry->d_inode))
-				mode &= ~current->fs->umask;
-			error = vfs_mkdir(nd.dentry->d_inode, dentry, mode);
-			dput(dentry);
-		}
-		mutex_unlock(&nd.dentry->d_inode->i_mutex);
-		path_release(&nd);
-out:
-		putname(tmp);
-	}
+	error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
+	if (error)
+		goto out;
+	dentry = lookup_create(&nd, 1);
+	error = PTR_ERR(dentry);
+	if (IS_ERR(dentry))
+		goto out_unlock;
 
+	if (!IS_POSIXACL(nd.dentry->d_inode))
+		mode &= ~current->fs->umask;
+	error = vfs_mkdir(nd.dentry->d_inode, dentry, mode);
+	dput(dentry);
+out_unlock:
+	mutex_unlock(&nd.dentry->d_inode->i_mutex);
+	path_release(&nd);
+out:
+	putname(tmp);
+out_err:
 	return error;
 }
 
@@ -2048,10 +2050,11 @@ static long do_rmdir(int dfd, const char
 	mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
 	dentry = lookup_hash(&nd);
 	error = PTR_ERR(dentry);
-	if (!IS_ERR(dentry)) {
-		error = vfs_rmdir(nd.dentry->d_inode, dentry);
-		dput(dentry);
-	}
+	if (IS_ERR(dentry))
+		goto exit2;
+	error = vfs_rmdir(nd.dentry->d_inode, dentry);
+	dput(dentry);
+exit2:
 	mutex_unlock(&nd.dentry->d_inode->i_mutex);
 exit1:
 	path_release(&nd);
@@ -2191,30 +2194,33 @@ asmlinkage long sys_symlinkat(const char
 	int error = 0;
 	char * from;
 	char * to;
+	struct dentry *dentry;
+	struct nameidata nd;
 
 	from = getname(oldname);
 	if(IS_ERR(from))
 		return PTR_ERR(from);
 	to = getname(newname);
 	error = PTR_ERR(to);
-	if (!IS_ERR(to)) {
-		struct dentry *dentry;
-		struct nameidata nd;
+	if (IS_ERR(to))
+		goto out_putname;
 
-		error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
-		if (error)
-			goto out;
-		dentry = lookup_create(&nd, 0);
-		error = PTR_ERR(dentry);
-		if (!IS_ERR(dentry)) {
-			error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO);
-			dput(dentry);
-		}
-		mutex_unlock(&nd.dentry->d_inode->i_mutex);
-		path_release(&nd);
+	error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
+	if (error)
+		goto out;
+	dentry = lookup_create(&nd, 0);
+	error = PTR_ERR(dentry);
+	if (IS_ERR(dentry))
+		goto out_unlock;
+
+	error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO);
+	dput(dentry);
+out_unlock:
+	mutex_unlock(&nd.dentry->d_inode->i_mutex);
+	path_release(&nd);
 out:
-		putname(to);
-	}
+	putname(to);
+out_putname:
 	putname(from);
 	return error;
 }
@@ -2300,10 +2306,11 @@ asmlinkage long sys_linkat(int olddfd, c
 		goto out_release;
 	new_dentry = lookup_create(&nd, 0);
 	error = PTR_ERR(new_dentry);
-	if (!IS_ERR(new_dentry)) {
-		error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
-		dput(new_dentry);
-	}
+	if (IS_ERR(new_dentry))
+		goto out_unlock;
+	error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
+	dput(new_dentry);
+out_unlock:
 	mutex_unlock(&nd.dentry->d_inode->i_mutex);
 out_release:
 	path_release(&nd);
fs/open.c renamed from fs/open.c~B1-prepwork-collapse-ifs +37 -27

97 lines not on this page · raw message

_

Acked-by on v1 1/6
#2 lore raw

Re: [PATCH 1/6] prepare for write access checks: collapse if()

Quoted 8 lines · Dave Hansen

We're shortly going to be adding a bunch more permission checks in these functions. That requires adding either a bunch of new if() conditions, or some gotos. This patch collapses existing if()s and uses gotos instead to prepare for the upcoming changes.

fs/open.c asmlinkage long sys_faccessat(int dfd, c · line 528

Quoted 3 lines
fs/open.c renamed from fs/open.c~B1-prepwork-collapse-ifs +3 -0
+	res = vfs_permission(&nd, mode);
+	/* SuS v2 requires we report a read only fs too */
+	if(res || !(mode & S_IWOTH) ||

except that there's a space missing after the if here :)

#3 lore raw

Re: [PATCH 1/6] prepare for write access checks: collapse if()

Quoted 11 lines · Christoph Hellwig
Quoted 8 lines · Dave Hansen

We're shortly going to be adding a bunch more permission checks in these functions. That requires adding either a bunch of new if() conditions, or some gotos. This patch collapses existing if()s and uses gotos instead to prepare for the upcoming changes.

Quoted 7 lines

Review

Quoted 3 lines
(quoted diff) +3 -0
+	res = vfs_permission(&nd, mode);
+	/* SuS v2 requires we report a read only fs too */
+	if(res || !(mode & S_IWOTH) ||

except that there's a space missing after the if here :)

In my defense, the code I moved sucked, too. ;)

---

 lxc-dave/fs/open.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff -puN fs/namei.c~B1-prepwork-collapse-ifs-spacefix fs/namei.c

fs/open.c renamed from fs/open.c~B1-prepwork-collapse-ifs-spacefix +2 -2
diff -puN fs/open.c~B1-prepwork-collapse-ifs-spacefix fs/open.c
@@ -525,11 +525,11 @@ asmlinkage long sys_faccessat(int dfd, c
 
 	res = vfs_permission(&nd, mode);
 	/* SuS v2 requires we report a read only fs too */
-	if(res || !(mode & S_IWOTH) ||
+	if (res || !(mode & S_IWOTH) ||
 	   special_file(nd.dentry->d_inode->i_mode))
 		goto out_path_release;
 
-	if(IS_RDONLY(nd.dentry->d_inode))
+	if (IS_RDONLY(nd.dentry->d_inode))
 		res = -EROFS;
 
 out_path_release:

_

-- Dave

#4 lore raw

PATCH 2/6 r/o bind mount prepwork: move open_namei()'s vfs_create()

In reply to Dave Hansen

The code around vfs_create() in open_namei() is getting a bit too complex. Right now, there is at least the reference count on the dentry, and the i_mutex to worry about. Soon, we'll also have mnt_writecount.

So, break the vfs_create() call out of open_namei(), and into a helper function. This duplicates the call to may_open(), but that isn't such a bad thing since the arguments (acc_mode and flag) were being heavily massaged anyway.

---

 lxc-dave/fs/namei.c |   30 ++++++++++++++++++++----------
 1 files changed, 20 insertions(+), 10 deletions(-)
fs/namei.c renamed from fs/namei.c~B2-prepwork-cleanup-open_namei +20 -10
diff -puN fs/namei.c~B2-prepwork-cleanup-open_namei fs/namei.c
@@ -1587,6 +1587,24 @@ int may_open(struct nameidata *nd, int a
 	return 0;
 }
 
+static int open_namei_create(struct nameidata *nd, struct path *path,
+				int flag, int mode)
+{
+	int error;
+	struct dentry *dir = nd->dentry;
+
+	if (!IS_POSIXACL(dir->d_inode))
+		mode &= ~current->fs->umask;
+	error = vfs_create(dir->d_inode, path->dentry, mode, nd);
+	mutex_unlock(&dir->d_inode->i_mutex);
+	dput(nd->dentry);
+	nd->dentry = path->dentry;
+	if (error)
+		return error;
+	/* Don't check for write permission, don't truncate */
+	return may_open(nd, 0, flag & ~O_TRUNC);
+}
+
 /*
  *	open_namei()
  *
@@ -1668,18 +1686,10 @@ do_last:
 
 	/* Negative dentry, just create the file */
 	if (!path.dentry->d_inode) {
-		if (!IS_POSIXACL(dir->d_inode))
-			mode &= ~current->fs->umask;
-		error = vfs_create(dir->d_inode, path.dentry, mode, nd);
-		mutex_unlock(&dir->d_inode->i_mutex);
-		dput(nd->dentry);
-		nd->dentry = path.dentry;
+		error = open_namei_create(nd, &path, flag, mode);
 		if (error)
 			goto exit;
-		/* Don't check for write permission, don't truncate */
-		acc_mode = 0;
-		flag &= ~O_TRUNC;
-		goto ok;
+		return 0;
 	}
 
 	/*

_

Acked-by on v1 2/6
#5 lore raw

Re: [PATCH 2/6] r/o bind mount prepwork: move open_namei()'s vfs_create()

Quoted 6 lines · Dave Hansen

The code around vfs_create() in open_namei() is getting a bit too complex. Right now, there is at least the reference count on the dentry, and the i_mutex to worry about. Soon, we'll also have mnt_writecount.

Quoted 7 lines

So, break the vfs_create() call out of open_namei(), and into a helper function. This duplicates the call to may_open(), but that isn't such a bad thing since the arguments (acc_mode and flag) were being heavily massaged anyway.

#6 lore raw

PATCH 3/6 unlink: monitor i_nlink

In reply to Dave Hansen

When a filesystem decrements i_nlink to zero, it means that a write must be performed in order to drop the inode from the filesystem.

We're shortly going to have keep filesystems from being remounted r/o between the time that this i_nlink decrement and that write occurs.

So, add a little helper function to do the decrements. We'll tie into it in a bit to note when i_nlink hits zero.

---

 lxc-dave/drivers/usb/core/inode.c |    7 ++++---
 lxc-dave/fs/autofs/root.c         |    2 +-
 lxc-dave/fs/autofs4/root.c        |    2 +-
 lxc-dave/fs/bfs/dir.c             |    9 +++------
 lxc-dave/fs/cifs/inode.c          |   10 +++++-----
 lxc-dave/fs/coda/dir.c            |    4 ++--
 lxc-dave/fs/ext2/namei.c          |    2 +-
 lxc-dave/fs/ext3/namei.c          |   14 +++++++-------
 lxc-dave/fs/hfs/dir.c             |    2 +-
 lxc-dave/fs/hfsplus/dir.c         |    2 +-
 lxc-dave/fs/hpfs/namei.c          |    6 +++---
 lxc-dave/fs/jffs/inode-v23.c      |    3 +--
 lxc-dave/fs/jffs2/dir.c           |    6 +++---
 lxc-dave/fs/jfs/namei.c           |   14 ++++++--------
 lxc-dave/fs/libfs.c               |   10 +++++-----
 lxc-dave/fs/minix/namei.c         |    2 +-
 lxc-dave/fs/msdos/namei.c         |    9 ++++-----
 lxc-dave/fs/nfs/dir.c             |    6 +++---
 lxc-dave/fs/ocfs2/namei.c         |    4 ++--
 lxc-dave/fs/qnx4/namei.c          |    6 ++----
 lxc-dave/fs/reiserfs/namei.c      |    6 +++---
 lxc-dave/fs/sysv/namei.c          |    2 +-
 lxc-dave/fs/udf/namei.c           |   18 ++++++------------
 lxc-dave/fs/ufs/namei.c           |    2 +-
 lxc-dave/fs/vfat/namei.c          |    9 ++++-----
 lxc-dave/include/linux/fs.h       |    7 ++++++-
 lxc-dave/ipc/mqueue.c             |    2 +-
 lxc-dave/mm/shmem.c               |   10 +++++-----
 28 files changed, 83 insertions(+), 93 deletions(-)
include/linux/fs.h renamed from include/linux/fs.h~B3-unlink-monitor-i_nlink-0 +6 -1
diff -puN include/linux/fs.h~B3-unlink-monitor-i_nlink-0 include/linux/fs.h
@@ -1262,9 +1262,14 @@ static inline void inode_inc_link_count(
 	mark_inode_dirty(inode);
 }
 
-static inline void inode_dec_link_count(struct inode *inode)
+static inline void drop_nlink(struct inode *inode)
 {
 	inode->i_nlink--;
+}
+
+static inline void inode_dec_link_count(struct inode *inode)
+{
+	drop_nlink(inode);
 	mark_inode_dirty(inode);
 }
 
drivers/usb/core/inode.c renamed from drivers/usb/core/inode.c~B3-unlink-monitor-i_nlink-0 +4 -3
diff -puN drivers/usb/core/inode.c~B3-unlink-monitor-i_nlink-0 drivers/usb/core/inode.c
@@ -332,7 +332,7 @@ static int usbfs_unlink (struct inode *d
 {
 	struct inode *inode = dentry->d_inode;
 	mutex_lock(&inode->i_mutex);
-	dentry->d_inode->i_nlink--;
+	drop_nlink(dentry->d_inode);
 	dput(dentry);
 	mutex_unlock(&inode->i_mutex);
 	d_delete(dentry);
@@ -347,10 +347,11 @@ static int usbfs_rmdir(struct inode *dir
 	mutex_lock(&inode->i_mutex);
 	dentry_unhash(dentry);
 	if (usbfs_empty(dentry)) {
-		dentry->d_inode->i_nlink -= 2;
+		drop_nlink(dentry->d_inode);
+		drop_nlink(dentry->d_inode);
 		dput(dentry);
 		inode->i_flags |= S_DEAD;
-		dir->i_nlink--;
+		drop_nlink(dir);
 		error = 0;
 	}
 	mutex_unlock(&inode->i_mutex);
fs/autofs/root.c renamed from fs/autofs/root.c~B3-unlink-monitor-i_nlink-0 +1 -1
diff -puN fs/autofs/root.c~B3-unlink-monitor-i_nlink-0 fs/autofs/root.c
@@ -414,7 +414,7 @@ static int autofs_root_rmdir(struct inod
 
 	dentry->d_time = (unsigned long)(struct autofs_dir_ent *)NULL;
 	autofs_hash_delete(ent);
-	dir->i_nlink--;
+	drop_nlink(dir);
 	d_drop(dentry);
 	unlock_kernel();
 
fs/autofs4/root.c renamed from fs/autofs4/root.c~B3-unlink-monitor-i_nlink-0 +1 -1
diff -puN fs/autofs4/root.c~B3-unlink-monitor-i_nlink-0 fs/autofs4/root.c
@@ -676,7 +676,7 @@ static int autofs4_dir_rmdir(struct inod
 	dentry->d_inode->i_nlink = 0;
 
 	if (dir->i_nlink)
-		dir->i_nlink--;
+		drop_nlink(dir);
 
 	return 0;
 }
fs/bfs/dir.c renamed from fs/bfs/dir.c~B3-unlink-monitor-i_nlink-0 +3 -6
diff -puN fs/bfs/dir.c~B3-unlink-monitor-i_nlink-0 fs/bfs/dir.c
@@ -117,8 +117,7 @@ static int bfs_create(struct inode * dir
 
 	err = bfs_add_entry(dir, dentry->d_name.name, dentry->d_name.len, inode->i_ino);
 	if (err) {
-		inode->i_nlink--;
-		mark_inode_dirty(inode);
+		inode_dec_link_count(inode);
 		iput(inode);
 		unlock_kernel();
 		return err;
@@ -196,9 +195,8 @@ static int bfs_unlink(struct inode * dir
 	mark_buffer_dirty(bh);
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
 	mark_inode_dirty(dir);
-	inode->i_nlink--;
 	inode->i_ctime = dir->i_ctime;
-	mark_inode_dirty(inode);
+	inode_dec_link_count(inode);
 	error = 0;
 
 out_brelse:
@@ -249,9 +247,8 @@ static int bfs_rename(struct inode * old
 	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
 	mark_inode_dirty(old_dir);
 	if (new_inode) {
-		new_inode->i_nlink--;
 		new_inode->i_ctime = CURRENT_TIME_SEC;
-		mark_inode_dirty(new_inode);
+		inode_dec_link_count(new_inode);
 	}
 	mark_buffer_dirty(old_bh);
 	error = 0;
fs/cifs/inode.c renamed from fs/cifs/inode.c~B3-unlink-monitor-i_nlink-0 +5 -5
diff -puN fs/cifs/inode.c~B3-unlink-monitor-i_nlink-0 fs/cifs/inode.c
@@ -591,7 +591,7 @@ int cifs_unlink(struct inode *inode, str
 
 	if (!rc) {
 		if (direntry->d_inode)
-			direntry->d_inode->i_nlink--;
+			drop_nlink(direntry->d_inode);
 	} else if (rc == -ENOENT) {
 		d_drop(direntry);
 	} else if (rc == -ETXTBSY) {
@@ -610,7 +610,7 @@ int cifs_unlink(struct inode *inode, str
 						CIFS_MOUNT_MAP_SPECIAL_CHR);
 			CIFSSMBClose(xid, pTcon, netfid);
 			if (direntry->d_inode)
-				direntry->d_inode->i_nlink--;
+				drop_nlink(direntry->d_inode);
 		}
 	} else if (rc == -EACCES) {
 		/* try only if r/o attribute set in local lookup data? */
@@ -664,7 +664,7 @@ int cifs_unlink(struct inode *inode, str
 						CIFS_MOUNT_MAP_SPECIAL_CHR);
 			if (!rc) {
 				if (direntry->d_inode)
-					direntry->d_inode->i_nlink--;
+					drop_nlink(direntry->d_inode);
 			} else if (rc == -ETXTBSY) {
 				int oplock = FALSE;
 				__u16 netfid;
@@ -685,7 +685,7 @@ int cifs_unlink(struct inode *inode, str
 						    CIFS_MOUNT_MAP_SPECIAL_CHR);
 					CIFSSMBClose(xid, pTcon, netfid);
 					if (direntry->d_inode)
-			                        direntry->d_inode->i_nlink--;
+						drop_nlink(direntry->d_inode);
 				}
 			/* BB if rc = -ETXTBUSY goto the rename logic BB */
 			}
@@ -817,7 +817,7 @@ int cifs_rmdir(struct inode *inode, stru
 			  cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
 
 	if (!rc) {
-		inode->i_nlink--;
+		drop_nlink(inode);
 		i_size_write(direntry->d_inode,0);
 		direntry->d_inode->i_nlink = 0;
 	}
fs/coda/dir.c renamed from fs/coda/dir.c~B3-unlink-monitor-i_nlink-0 +2 -2
diff -puN fs/coda/dir.c~B3-unlink-monitor-i_nlink-0 fs/coda/dir.c
@@ -367,7 +367,7 @@ int coda_unlink(struct inode *dir, struc
         }
 
 	coda_dir_changed(dir, 0);
-	de->d_inode->i_nlink--;
+	drop_nlink(de->d_inode);
 	unlock_kernel();
 
         return 0;
@@ -394,7 +394,7 @@ int coda_rmdir(struct inode *dir, struct
         }
 
 	coda_dir_changed(dir, -1);
-	de->d_inode->i_nlink--;
+	drop_nlink(de->d_inode);
 	d_delete(de);
 	unlock_kernel();
 
fs/ext2/namei.c renamed from fs/ext2/namei.c~B3-unlink-monitor-i_nlink-0 +1 -1
diff -puN fs/ext2/namei.c~B3-unlink-monitor-i_nlink-0 fs/ext2/namei.c
@@ -326,7 +326,7 @@ static int ext2_rename (struct inode * o
 		ext2_set_link(new_dir, new_de, new_page, old_inode);
 		new_inode->i_ctime = CURRENT_TIME_SEC;
 		if (dir_de)
-			new_inode->i_nlink--;
+			drop_nlink(new_inode);
 		inode_dec_link_count(new_inode);
 	} else {
 		if (dir_de) {
fs/ext3/namei.c renamed from fs/ext3/namei.c~B3-unlink-monitor-i_nlink-0 +7 -7
diff -puN fs/ext3/namei.c~B3-unlink-monitor-i_nlink-0 fs/ext3/namei.c
@@ -1621,7 +1621,7 @@ static inline void ext3_inc_count(handle
 
 static inline void ext3_dec_count(handle_t *handle, struct inode *inode)
 {
-	inode->i_nlink--;
+	drop_nlink(inode);
 }
 
 static int ext3_add_nondir(handle_t *handle,
@@ -1743,7 +1743,7 @@ retry:
 	inode->i_size = EXT3_I(inode)->i_disksize = inode->i_sb->s_blocksize;
 	dir_block = ext3_bread (handle, inode, 0, 1, &err);
 	if (!dir_block) {
-		inode->i_nlink--; /* is this nlink == 0? */
+		drop_nlink(inode); /* is this nlink == 0? */
 		ext3_mark_inode_dirty(handle, inode);
 		iput (inode);
 		goto out_stop;
@@ -2053,7 +2053,7 @@ static int ext3_rmdir (struct inode * di
 	ext3_orphan_add(handle, inode);
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
 	ext3_mark_inode_dirty(handle, inode);
-	dir->i_nlink--;
+	drop_nlink(dir);
 	ext3_update_dx_flag(dir);
 	ext3_mark_inode_dirty(handle, dir);
 
@@ -2104,7 +2104,7 @@ static int ext3_unlink(struct inode * di
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
 	ext3_update_dx_flag(dir);
 	ext3_mark_inode_dirty(handle, dir);
-	inode->i_nlink--;
+	drop_nlink(inode);
 	if (!inode->i_nlink)
 		ext3_orphan_add(handle, inode);
 	inode->i_ctime = dir->i_ctime;
@@ -2326,7 +2326,7 @@ static int ext3_rename (struct inode * o
 	}
 
 	if (new_inode) {
-		new_inode->i_nlink--;
+		drop_nlink(new_inode);
 		new_inode->i_ctime = CURRENT_TIME_SEC;
 	}
 	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
@@ -2337,9 +2337,9 @@ static int ext3_rename (struct inode * o
 		PARENT_INO(dir_bh->b_data) = cpu_to_le32(new_dir->i_ino);
 		BUFFER_TRACE(dir_bh, "call ext3_journal_dirty_metadata");
 		ext3_journal_dirty_metadata(handle, dir_bh);
-		old_dir->i_nlink--;
+		drop_nlink(old_dir);
 		if (new_inode) {
-			new_inode->i_nlink--;
+			drop_nlink(new_inode);
 		} else {
 			new_dir->i_nlink++;
 			ext3_update_dx_flag(new_dir);
fs/hfs/dir.c renamed from fs/hfs/dir.c~B3-unlink-monitor-i_nlink-0 +1 -1
diff -puN fs/hfs/dir.c~B3-unlink-monitor-i_nlink-0 fs/hfs/dir.c
@@ -246,7 +246,7 @@ static int hfs_unlink(struct inode *dir,
 	if (res)
 		return res;
 
-	inode->i_nlink--;
+	drop_nlink(inode);
 	hfs_delete_inode(inode);
 	inode->i_ctime = CURRENT_TIME_SEC;
 	mark_inode_dirty(inode);
fs/hfsplus/dir.c renamed from fs/hfsplus/dir.c~B3-unlink-monitor-i_nlink-0 +1 -1
diff -puN fs/hfsplus/dir.c~B3-unlink-monitor-i_nlink-0 fs/hfsplus/dir.c
@@ -338,7 +338,7 @@ static int hfsplus_unlink(struct inode *
 		return res;
 
 	if (inode->i_nlink > 0)
-		inode->i_nlink--;
+		drop_nlink(inode);
 	hfsplus_delete_inode(inode);
 	if (inode->i_ino != cnid && !inode->i_nlink) {
 		if (!atomic_read(&HFSPLUS_I(inode).opencnt)) {
fs/hpfs/namei.c renamed from fs/hpfs/namei.c~B3-unlink-monitor-i_nlink-0 +3 -3
diff -puN fs/hpfs/namei.c~B3-unlink-monitor-i_nlink-0 fs/hpfs/namei.c
@@ -434,7 +434,7 @@ again:
 		unlock_kernel();
 		return -ENOSPC;
 	default:
-		inode->i_nlink--;
+		drop_nlink(inode);
 		err = 0;
 	}
 	goto out;
@@ -494,7 +494,7 @@ static int hpfs_rmdir(struct inode *dir,
 		err = -ENOSPC;
 		break;
 	default:
-		dir->i_nlink--;
+		drop_nlink(dir);
 		inode->i_nlink = 0;
 		err = 0;
 	}
@@ -636,7 +636,7 @@ static int hpfs_rename(struct inode *old
 	hpfs_i(i)->i_parent_dir = new_dir->i_ino;
 	if (S_ISDIR(i->i_mode)) {
 		new_dir->i_nlink++;
-		old_dir->i_nlink--;
+		drop_nlink(old_dir);
 	}
 	if ((fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
 		fnode->up = new_dir->i_ino;
fs/jffs2/dir.c renamed from fs/jffs2/dir.c~B3-unlink-monitor-i_nlink-0 +3 -3
diff -puN fs/jffs2/dir.c~B3-unlink-monitor-i_nlink-0 fs/jffs2/dir.c
@@ -615,7 +615,7 @@ static int jffs2_rmdir (struct inode *di
 	}
 	ret = jffs2_unlink(dir_i, dentry);
 	if (!ret)
-		dir_i->i_nlink--;
+		drop_nlink(dir_i);
 	return ret;
 }
 
@@ -823,7 +823,7 @@ static int jffs2_rename (struct inode *o
 
 	if (victim_f) {
 		/* There was a victim. Kill it off nicely */
-		new_dentry->d_inode->i_nlink--;
+		drop_nlink(new_dentry->d_inode);
 		/* Don't oops if the victim was a dirent pointing to an
 		   inode which didn't exist. */
 		if (victim_f->inocache) {
@@ -862,7 +862,7 @@ static int jffs2_rename (struct inode *o
 	}
 
 	if (S_ISDIR(old_dentry->d_inode->i_mode))
-		old_dir_i->i_nlink--;
+		drop_nlink(old_dir_i);
 
 	new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now);
 
fs/jfs/namei.c renamed from fs/jfs/namei.c~B3-unlink-monitor-i_nlink-0 +6 -8
diff -puN fs/jfs/namei.c~B3-unlink-monitor-i_nlink-0 fs/jfs/namei.c
@@ -393,9 +393,8 @@ static int jfs_rmdir(struct inode *dip, 
 	/* update parent directory's link count corresponding
 	 * to ".." entry of the target directory deleted
 	 */
-	dip->i_nlink--;
 	dip->i_ctime = dip->i_mtime = CURRENT_TIME;
-	mark_inode_dirty(dip);
+	inode_dec_link_count(dip);
 
 	/*
 	 * OS/2 could have created EA and/or ACL
@@ -515,8 +514,7 @@ static int jfs_unlink(struct inode *dip,
 	mark_inode_dirty(dip);
 
 	/* update target's inode */
-	ip->i_nlink--;
-	mark_inode_dirty(ip);
+	inode_dec_link_count(ip);
 
 	/*
 	 *      commit zero link count object
@@ -835,7 +833,7 @@ static int jfs_link(struct dentry *old_d
 	rc = txCommit(tid, 2, &iplist[0], 0);
 
 	if (rc) {
-		ip->i_nlink--;
+		ip->i_nlink--; /* never instantiated */
 		iput(ip);
 	} else
 		d_instantiate(dentry, ip);
@@ -1155,9 +1153,9 @@ static int jfs_rename(struct inode *old_
 			      old_ip->i_ino, JFS_RENAME);
 		if (rc)
 			goto out4;
-		new_ip->i_nlink--;
+		drop_nlink(new_ip);
 		if (S_ISDIR(new_ip->i_mode)) {
-			new_ip->i_nlink--;
+			drop_nlink(new_ip);
 			if (new_ip->i_nlink) {
 				mutex_unlock(&JFS_IP(new_ip)->commit_mutex);
 				if (old_dir != new_dir)
@@ -1223,7 +1221,7 @@ static int jfs_rename(struct inode *old_
 		goto out4;
 	}
 	if (S_ISDIR(old_ip->i_mode)) {
-		old_dir->i_nlink--;
+		drop_nlink(old_dir);
 		if (old_dir != new_dir) {
 			/*
 			 * Change inode number of parent for moved directory
fs/libfs.c renamed from fs/libfs.c~B3-unlink-monitor-i_nlink-0 +5 -5
diff -puN fs/libfs.c~B3-unlink-monitor-i_nlink-0 fs/libfs.c
@@ -275,7 +275,7 @@ int simple_unlink(struct inode *dir, str
 	struct inode *inode = dentry->d_inode;
 
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	inode->i_nlink--;
+	drop_nlink(inode);
 	dput(dentry);
 	return 0;
 }
@@ -285,9 +285,9 @@ int simple_rmdir(struct inode *dir, stru
 	if (!simple_empty(dentry))
 		return -ENOTEMPTY;
 
-	dentry->d_inode->i_nlink--;
+	drop_nlink(dentry->d_inode);
 	simple_unlink(dir, dentry);
-	dir->i_nlink--;
+	drop_nlink(dir);
 	return 0;
 }
 
@@ -303,9 +303,9 @@ int simple_rename(struct inode *old_dir,
 	if (new_dentry->d_inode) {
 		simple_unlink(new_dir, new_dentry);
 		if (they_are_dirs)
-			old_dir->i_nlink--;
+			drop_nlink(old_dir);
 	} else if (they_are_dirs) {
-		old_dir->i_nlink--;
+		drop_nlink(old_dir);
 		new_dir->i_nlink++;
 	}
 
fs/nfs/dir.c renamed from fs/nfs/dir.c~B3-unlink-monitor-i_nlink-0 +3 -3
diff -puN fs/nfs/dir.c~B3-unlink-monitor-i_nlink-0 fs/nfs/dir.c
@@ -842,7 +842,7 @@ static void nfs_dentry_iput(struct dentr
 	nfs_inode_return_delegation(inode);
 	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
 		lock_kernel();
-		inode->i_nlink--;
+		drop_nlink(inode);
 		nfs_complete_unlink(dentry);
 		unlock_kernel();
 	}
@@ -1395,7 +1395,7 @@ static int nfs_safe_remove(struct dentry
 		error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
 		/* The VFS may want to delete this inode */
 		if (error == 0)
-			inode->i_nlink--;
+			drop_nlink(inode);
 		nfs_mark_for_revalidate(inode);
 		nfs_end_data_update(inode);
 	} else
@@ -1599,7 +1599,7 @@ static int nfs_rename(struct inode *old_
 			goto out;
 		}
 	} else
-		new_inode->i_nlink--;
+		drop_nlink(new_inode);
 
 go_ahead:
 	/*
fs/ocfs2/namei.c renamed from fs/ocfs2/namei.c~B3-unlink-monitor-i_nlink-0 +2 -2
diff -puN fs/ocfs2/namei.c~B3-unlink-monitor-i_nlink-0 fs/ocfs2/namei.c
@@ -711,7 +711,7 @@ static int ocfs2_link(struct dentry *old
 	err = ocfs2_journal_dirty(handle, fe_bh);
 	if (err < 0) {
 		le16_add_cpu(&fe->i_links_count, -1);
-		inode->i_nlink--;
+		drop_nlink(inode);
 		mlog_errno(err);
 		goto bail;
 	}
@@ -721,7 +721,7 @@ static int ocfs2_link(struct dentry *old
 			      parent_fe_bh, de_bh);
 	if (err) {
 		le16_add_cpu(&fe->i_links_count, -1);
-		inode->i_nlink--;
+		drop_nlink(inode);
 		mlog_errno(err);
 		goto bail;
 	}
fs/qnx4/namei.c renamed from fs/qnx4/namei.c~B3-unlink-monitor-i_nlink-0 +2 -4
diff -puN fs/qnx4/namei.c~B3-unlink-monitor-i_nlink-0 fs/qnx4/namei.c
@@ -189,8 +189,7 @@ int qnx4_rmdir(struct inode *dir, struct
 	inode->i_nlink = 0;
 	mark_inode_dirty(inode);
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
-	dir->i_nlink--;
-	mark_inode_dirty(dir);
+	inode_dec_link_count(dir);
 	retval = 0;
 
       end_rmdir:
@@ -234,9 +233,8 @@ int qnx4_unlink(struct inode *dir, struc
 	mark_buffer_dirty(bh);
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
 	mark_inode_dirty(dir);
-	inode->i_nlink--;
 	inode->i_ctime = dir->i_ctime;
-	mark_inode_dirty(inode);
+	inode_dec_link_count(inode);
 	retval = 0;
 
 end_unlink:
fs/reiserfs/namei.c renamed from fs/reiserfs/namei.c~B3-unlink-monitor-i_nlink-0 +3 -3
diff -puN fs/reiserfs/namei.c~B3-unlink-monitor-i_nlink-0 fs/reiserfs/namei.c
@@ -20,7 +20,7 @@
 #include <linux/quotaops.h>
 
 #define INC_DIR_INODE_NLINK(i) if (i->i_nlink != 1) { i->i_nlink++; if (i->i_nlink >= REISERFS_LINK_MAX) i->i_nlink=1; }
-#define DEC_DIR_INODE_NLINK(i) if (i->i_nlink != 1) i->i_nlink--;
+#define DEC_DIR_INODE_NLINK(i) if (i->i_nlink != 1) drop_nlink(i);
 
 // directory item contains array of entry headers. This performs
 // binary search through that array
@@ -994,7 +994,7 @@ static int reiserfs_unlink(struct inode 
 		inode->i_nlink = 1;
 	}
 
-	inode->i_nlink--;
+	drop_nlink(inode);
 
 	/*
 	 * we schedule before doing the add_save_link call, save the link
@@ -1475,7 +1475,7 @@ static int reiserfs_rename(struct inode 
 		if (S_ISDIR(new_dentry_inode->i_mode)) {
 			new_dentry_inode->i_nlink = 0;
 		} else {
-			new_dentry_inode->i_nlink--;
+			drop_nlink(new_dentry_inode);
 		}
 		new_dentry_inode->i_ctime = ctime;
 		savelink = new_dentry_inode->i_nlink;
fs/sysv/namei.c renamed from fs/sysv/namei.c~B3-unlink-monitor-i_nlink-0 +1 -1
diff -puN fs/sysv/namei.c~B3-unlink-monitor-i_nlink-0 fs/sysv/namei.c
@@ -250,7 +250,7 @@ static int sysv_rename(struct inode * ol
 		sysv_set_link(new_de, new_page, old_inode);
 		new_inode->i_ctime = CURRENT_TIME_SEC;
 		if (dir_de)
-			new_inode->i_nlink--;
+			drop_nlink(new_inode);
 		inode_dec_link_count(new_inode);
 	} else {
 		if (dir_de) {
fs/udf/namei.c renamed from fs/udf/namei.c~B3-unlink-monitor-i_nlink-0 +6 -12
diff -puN fs/udf/namei.c~B3-unlink-monitor-i_nlink-0 fs/udf/namei.c
@@ -878,8 +878,7 @@ static int udf_rmdir(struct inode * dir,
 			inode->i_nlink);
 	inode->i_nlink = 0;
 	inode->i_size = 0;
-	mark_inode_dirty(inode);
-	dir->i_nlink --;
+	inode_dec_link_count(inode);
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb);
 	mark_inode_dirty(dir);
 
@@ -923,8 +922,7 @@ static int udf_unlink(struct inode * dir
 		goto end_unlink;
 	dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb);
 	mark_inode_dirty(dir);
-	inode->i_nlink--;
-	mark_inode_dirty(inode);
+	inode_dec_link_count(inode);
 	inode->i_ctime = dir->i_ctime;
 	retval = 0;
 
@@ -1101,8 +1099,7 @@ out:
 	return err;
 
 out_no_entry:
-	inode->i_nlink--;
-	mark_inode_dirty(inode);
+	inode_dec_link_count(inode);
 	iput(inode);
 	goto out;
 }
@@ -1261,9 +1258,8 @@ static int udf_rename (struct inode * ol
 
 	if (new_inode)
 	{
-		new_inode->i_nlink--;
 		new_inode->i_ctime = current_fs_time(new_inode->i_sb);
-		mark_inode_dirty(new_inode);
+		inode_dec_link_count(new_inode);
 	}
 	old_dir->i_ctime = old_dir->i_mtime = current_fs_time(old_dir->i_sb);
 	mark_inode_dirty(old_dir);
@@ -1279,12 +1275,10 @@ static int udf_rename (struct inode * ol
 		}
 		else
 			mark_buffer_dirty_inode(dir_bh, old_inode);
-		old_dir->i_nlink --;
-		mark_inode_dirty(old_dir);
+		inode_dec_link_count(old_dir);
 		if (new_inode)
 		{
-			new_inode->i_nlink --;
-			mark_inode_dirty(new_inode);
+			inode_dec_link_count(new_inode);
 		}
 		else
 		{
fs/ufs/namei.c renamed from fs/ufs/namei.c~B3-unlink-monitor-i_nlink-0 +1 -1
diff -puN fs/ufs/namei.c~B3-unlink-monitor-i_nlink-0 fs/ufs/namei.c
@@ -308,7 +308,7 @@ static int ufs_rename(struct inode *old_
 		ufs_set_link(new_dir, new_de, new_page, old_inode);
 		new_inode->i_ctime = CURRENT_TIME_SEC;
 		if (dir_de)
-			new_inode->i_nlink--;
+			drop_nlink(new_inode);
 		inode_dec_link_count(new_inode);
 	} else {
 		if (dir_de) {
fs/vfat/namei.c renamed from fs/vfat/namei.c~B3-unlink-monitor-i_nlink-0 +4 -5
diff -puN fs/vfat/namei.c~B3-unlink-monitor-i_nlink-0 fs/vfat/namei.c
@@ -782,7 +782,7 @@ static int vfat_rmdir(struct inode *dir,
 	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
 	if (err)
 		goto out;
-	dir->i_nlink--;
+	drop_nlink(dir);
 
 	inode->i_nlink = 0;
 	inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
@@ -930,7 +930,7 @@ static int vfat_rename(struct inode *old
 			if (err)
 				goto error_dotdot;
 		}
-		old_dir->i_nlink--;
+		drop_nlink(old_dir);
 		if (!new_inode)
  			new_dir->i_nlink++;
 	}
@@ -947,10 +947,9 @@ static int vfat_rename(struct inode *old
 		mark_inode_dirty(old_dir);
 
 	if (new_inode) {
+		drop_nlink(new_inode);
 		if (is_dir)
-			new_inode->i_nlink -= 2;
-		else
-			new_inode->i_nlink--;
+			drop_nlink(new_inode);
 		new_inode->i_ctime = ts;
 	}
 out:
ipc/mqueue.c renamed from ipc/mqueue.c~B3-unlink-monitor-i_nlink-0 +1 -1
diff -puN ipc/mqueue.c~B3-unlink-monitor-i_nlink-0 ipc/mqueue.c
@@ -307,7 +307,7 @@ static int mqueue_unlink(struct inode *d
 
 	dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
 	dir->i_size -= DIRENT_SIZE;
-  	inode->i_nlink--;
+  	drop_nlink(inode);
   	dput(dentry);
   	return 0;
 }
mm/shmem.c renamed from mm/shmem.c~B3-unlink-monitor-i_nlink-0 +5 -5
diff -puN mm/shmem.c~B3-unlink-monitor-i_nlink-0 mm/shmem.c
@@ -1760,7 +1760,7 @@ static int shmem_unlink(struct inode *di
 
 	dir->i_size -= BOGO_DIRENT_SIZE;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	inode->i_nlink--;
+	drop_nlink(inode);
 	dput(dentry);	/* Undo the count from "create" - this does all the work */
 	return 0;
 }
@@ -1770,8 +1770,8 @@ static int shmem_rmdir(struct inode *dir
 	if (!simple_empty(dentry))
 		return -ENOTEMPTY;
 
-	dentry->d_inode->i_nlink--;
-	dir->i_nlink--;
+	drop_nlink(dentry->d_inode);
+	drop_nlink(dir);
 	return shmem_unlink(dir, dentry);
 }
 
@@ -1792,9 +1792,9 @@ static int shmem_rename(struct inode *ol
 	if (new_dentry->d_inode) {
 		(void) shmem_unlink(new_dir, new_dentry);
 		if (they_are_dirs)
-			old_dir->i_nlink--;
+			drop_nlink(old_dir);
 	} else if (they_are_dirs) {
-		old_dir->i_nlink--;
+		drop_nlink(old_dir);
 		new_dir->i_nlink++;
 	}
 

diff -puN fs/binfmt_misc.c~B3-unlink-monitor-i_nlink-0 fs/binfmt_misc.c

fs/jffs/inode-v23.c renamed from fs/jffs/inode-v23.c~B3-unlink-monitor-i_nlink-0 +1 -2
diff -puN fs/jffs/inode-v23.c~B3-unlink-monitor-i_nlink-0 fs/jffs/inode-v23.c
@@ -1052,9 +1052,8 @@ jffs_remove(struct inode *dir, struct de
 
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
 	mark_inode_dirty(dir);
-	inode->i_nlink--;
 	inode->i_ctime = dir->i_ctime;
-	mark_inode_dirty(inode);
+	inode_dec_link_count(inode);
 
 	d_delete(dentry);	/* This also frees the inode */
 
fs/minix/namei.c renamed from fs/minix/namei.c~B3-unlink-monitor-i_nlink-0 +1 -1
diff -puN fs/minix/namei.c~B3-unlink-monitor-i_nlink-0 fs/minix/namei.c
@@ -249,7 +249,7 @@ static int minix_rename(struct inode * o
 		minix_set_link(new_de, new_page, old_inode);
 		new_inode->i_ctime = CURRENT_TIME_SEC;
 		if (dir_de)
-			new_inode->i_nlink--;
+			drop_nlink(new_inode);
 		inode_dec_link_count(new_inode);
 	} else {
 		if (dir_de) {
fs/msdos/namei.c renamed from fs/msdos/namei.c~B3-unlink-monitor-i_nlink-0 +4 -5
diff -puN fs/msdos/namei.c~B3-unlink-monitor-i_nlink-0 fs/msdos/namei.c
@@ -341,7 +341,7 @@ static int msdos_rmdir(struct inode *dir
 	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
 	if (err)
 		goto out;
-	dir->i_nlink--;
+	drop_nlink(dir);
 
 	inode->i_nlink = 0;
 	inode->i_ctime = CURRENT_TIME_SEC;
@@ -542,7 +542,7 @@ static int do_msdos_rename(struct inode 
 			if (err)
 				goto error_dotdot;
 		}
-		old_dir->i_nlink--;
+		drop_nlink(old_dir);
 		if (!new_inode)
 			new_dir->i_nlink++;
 	}
@@ -559,10 +559,9 @@ static int do_msdos_rename(struct inode 
 		mark_inode_dirty(old_dir);
 
 	if (new_inode) {
+		drop_nlink(new_inode);
 		if (is_dir)
-			new_inode->i_nlink -= 2;
-		else
-			new_inode->i_nlink--;
+			drop_nlink(new_inode);
 		new_inode->i_ctime = ts;
 	}
 out:

_

Acked-by on v1 3/6
#7 lore raw

Re: [PATCH 3/6] unlink: monitor i_nlink

Quoted 5 lines · Dave Hansen

When a filesystem decrements i_nlink to zero, it means that a write must be performed in order to drop the inode from the filesystem.

Quoted 8 lines

We're shortly going to have keep filesystems from being remounted r/o between the time that this i_nlink decrement and that write occurs.

So, add a little helper function to do the decrements. We'll tie into it in a bit to note when i_nlink hits zero.

Note that we all (and especially Andrew :)) need to be carefull not to introduce unguarded i_nlink decrements again. Dave, you'll probably need to do another audit when you introduce the real functionality.

#8 lore raw

Re: [PATCH 3/6] unlink: monitor i_nlink

Quoted 13 lines · Christoph Hellwig
Quoted 12 lines · Dave Hansen

When a filesystem decrements i_nlink to zero, it means that a write must be performed in order to drop the inode from the filesystem.

We're shortly going to have keep filesystems from being remounted r/o between the time that this i_nlink decrement and that write occurs.

So, add a little helper function to do the decrements. We'll tie into it in a bit to note when i_nlink hits zero.

Quoted 7 lines
Quoted 1 line

Note that we all (and especially Andrew :)) need to be carefull not to introduce unguarded i_nlink decrements again. Dave, you'll probably need to do another audit when you introduce the real functionality.

Yup, I have my eyes on it. git and mm commits should help as well.

-- Dave

#9 lore raw

Re: [PATCH 3/6] unlink: monitor i_nlink

Quoted 16 lines · On Wed, 09 Aug 2006 10:17:19 -0700 Dave Hansen <[email protected]> wrote:
Quoted 15 lines · Christoph Hellwig
Quoted 13 lines · Dave Hansen

When a filesystem decrements i_nlink to zero, it means that a write must be performed in order to drop the inode from the filesystem.

We're shortly going to have keep filesystems from being remounted r/o between the time that this i_nlink decrement and that write occurs.

So, add a little helper function to do the decrements. We'll tie into it in a bit to note when i_nlink hits zero.

Quoted 8 lines
Quoted 5 lines

Note that we all (and especially Andrew :)) need to be carefull not to introduce unguarded i_nlink decrements again. Dave, you'll probably need to do another audit when you introduce the real functionality.

Yup, I have my eyes on it. git and mm commits should help as well.

A comprehensive way to do this sort of thing is to rename the field. We have >500 references, so one would do:

struct inode {
	...
	union {
		unsigned int i_nlink;
		unsigned int i_nlink_use_the_accessors_please;
	}
	...
};

then, when everything in-tree is migrated, remove `i_nlink'.

It's a bit of a hassle, but it will give the most reliable result for both in-tree and out-of-tree filesystems.

#10 lore raw

Re: [PATCH 3/6] unlink: monitor i_nlink

Quoted 10 lines · Andrew Morton

struct inode {
        ...
        union {
                unsigned int i_nlink;
                unsigned int i_nlink_use_the_accessors_please; }
        ...
};

then, when everything in-tree is migrated, remove `i_nlink'.

You can also mark a union member deprecated:

struct inode {
        ...
        union {
                unsigned int __deprecated i_nlink;
                unsigned int i_nlink_use_the_accessors_please;
        }
        ...
};

So gcc will warn about any newly introduced users.

	Arnd <><
#11 lore raw

PATCH 4/6 r/o bind mount prepwork: inc_nlink() helper

In reply to Dave Hansen

This is mostly included for parity with dec_nlink(), where we will have some more hooks. This one should stay pretty darn straightforward for now.

---

 lxc-dave/drivers/infiniband/hw/ipath/ipath_fs.c |    4 ++--
 lxc-dave/drivers/usb/core/inode.c               |    4 ++--
 lxc-dave/fs/9p/vfs_inode.c                      |    2 +-
 lxc-dave/fs/autofs/root.c                       |    2 +-
 lxc-dave/fs/autofs4/root.c                      |    2 +-
 lxc-dave/fs/bfs/dir.c                           |    2 +-
 lxc-dave/fs/cifs/inode.c                        |    2 +-
 lxc-dave/fs/coda/dir.c                          |    2 +-
 lxc-dave/fs/configfs/dir.c                      |    4 ++--
 lxc-dave/fs/configfs/mount.c                    |    2 +-
 lxc-dave/fs/debugfs/inode.c                     |    4 ++--
 lxc-dave/fs/ext3/namei.c                        |    6 +++---
 lxc-dave/fs/fuse/control.c                      |    2 +-
 lxc-dave/fs/hfsplus/dir.c                       |    2 +-
 lxc-dave/fs/hpfs/namei.c                        |    4 ++--
 lxc-dave/fs/hugetlbfs/inode.c                   |    4 ++--
 lxc-dave/fs/jffs2/dir.c                         |    6 +++---
 lxc-dave/fs/jffs2/fs.c                          |    6 +++---
 lxc-dave/fs/jfs/namei.c                         |    6 +++---
 lxc-dave/fs/libfs.c                             |    4 ++--
 lxc-dave/fs/msdos/namei.c                       |    4 ++--
 lxc-dave/fs/ocfs2/dlm/dlmfs.c                   |    6 +++---
 lxc-dave/fs/ocfs2/namei.c                       |    8 ++++----
 lxc-dave/fs/ramfs/inode.c                       |    4 ++--
 lxc-dave/fs/reiserfs/namei.c                    |    6 +++---
 lxc-dave/fs/sysfs/dir.c                         |    4 ++--
 lxc-dave/fs/sysfs/mount.c                       |    2 +-
 lxc-dave/fs/udf/inode.c                         |    2 +-
 lxc-dave/fs/udf/namei.c                         |    6 +++---
 lxc-dave/fs/vfat/namei.c                        |    4 ++--
 lxc-dave/include/linux/fs.h                     |    7 ++++++-
 lxc-dave/ipc/mqueue.c                           |    2 +-
 lxc-dave/kernel/cpuset.c                        |    8 ++++----
 lxc-dave/mm/shmem.c                             |    8 ++++----
 lxc-dave/net/sunrpc/rpc_pipe.c                  |    6 +++---
 lxc-dave/security/inode.c                       |    4 ++--
 lxc-dave/security/selinux/selinuxfs.c           |    4 ++--
 37 files changed, 80 insertions(+), 75 deletions(-)
include/linux/fs.h renamed from include/linux/fs.h~B4-monitor_inc_nlink +6 -1
diff -puN include/linux/fs.h~B4-monitor_inc_nlink include/linux/fs.h
@@ -1256,9 +1256,14 @@ static inline void mark_inode_dirty_sync
 	__mark_inode_dirty(inode, I_DIRTY_SYNC);
 }
 
-static inline void inode_inc_link_count(struct inode *inode)
+static inline void inc_nlink(struct inode *inode)
 {
 	inode->i_nlink++;
+}
+
+static inline void inode_inc_link_count(struct inode *inode)
+{
+	inc_nlink(inode);
 	mark_inode_dirty(inode);
 }
 
fs/libfs.c renamed from fs/libfs.c~B4-monitor_inc_nlink +2 -2
diff -puN fs/libfs.c~B4-monitor_inc_nlink fs/libfs.c
@@ -243,7 +243,7 @@ int simple_link(struct dentry *old_dentr
 	struct inode *inode = old_dentry->d_inode;
 
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	inode->i_nlink++;
+	inc_nlink(inode);
 	atomic_inc(&inode->i_count);
 	dget(dentry);
 	d_instantiate(dentry, inode);
@@ -306,7 +306,7 @@ int simple_rename(struct inode *old_dir,
 			drop_nlink(old_dir);
 	} else if (they_are_dirs) {
 		drop_nlink(old_dir);
-		new_dir->i_nlink++;
+		inc_nlink(new_dir);
 	}
 
 	old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime =
mm/shmem.c renamed from mm/shmem.c~B4-monitor_inc_nlink +4 -4
diff -puN mm/shmem.c~B4-monitor_inc_nlink mm/shmem.c
@@ -1371,7 +1371,7 @@ shmem_get_inode(struct super_block *sb, 
 							&sbinfo->policy_nodes);
 			break;
 		case S_IFDIR:
-			inode->i_nlink++;
+			inc_nlink(inode);
 			/* Some things misbehave if size == 0 on a directory */
 			inode->i_size = 2 * BOGO_DIRENT_SIZE;
 			inode->i_op = &shmem_dir_inode_operations;
@@ -1703,7 +1703,7 @@ static int shmem_mkdir(struct inode *dir
 
 	if ((error = shmem_mknod(dir, dentry, mode | S_IFDIR, 0)))
 		return error;
-	dir->i_nlink++;
+	inc_nlink(dir);
 	return 0;
 }
 
@@ -1738,7 +1738,7 @@ static int shmem_link(struct dentry *old
 
 	dir->i_size += BOGO_DIRENT_SIZE;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	inode->i_nlink++;
+	inc_nlink(inode);
 	atomic_inc(&inode->i_count);	/* New dentry reference */
 	dget(dentry);		/* Extra pinning count for the created dentry */
 	d_instantiate(dentry, inode);
@@ -1795,7 +1795,7 @@ static int shmem_rename(struct inode *ol
 			drop_nlink(old_dir);
 	} else if (they_are_dirs) {
 		drop_nlink(old_dir);
-		new_dir->i_nlink++;
+		inc_nlink(new_dir);
 	}
 
 	old_dir->i_size -= BOGO_DIRENT_SIZE;
drivers/infiniband/hw/ipath/ipath_fs.c renamed from drivers/infiniband/hw/ipath/ipath_fs.c~B4-monitor_inc_nlink +2 -2
diff -puN drivers/infiniband/hw/ipath/ipath_fs.c~B4-monitor_inc_nlink drivers/infiniband/hw/ipath/ipath_fs.c
@@ -66,8 +66,8 @@ static int ipathfs_mknod(struct inode *d
 	inode->i_private = data;
 	if ((mode & S_IFMT) == S_IFDIR) {
 		inode->i_op = &simple_dir_inode_operations;
-		inode->i_nlink++;
-		dir->i_nlink++;
+		inc_nlink(inode);
+		inc_nlink(dir);
 	}
 
 	inode->i_fop = fops;
drivers/usb/core/inode.c renamed from drivers/usb/core/inode.c~B4-monitor_inc_nlink +2 -2
diff -puN drivers/usb/core/inode.c~B4-monitor_inc_nlink drivers/usb/core/inode.c
@@ -263,7 +263,7 @@ static struct inode *usbfs_get_inode (st
 			inode->i_fop = &simple_dir_operations;
 
 			/* directory inodes start off with i_nlink == 2 (for "." entry) */
-			inode->i_nlink++;
+			inc_nlink(inode);
 			break;
 		}
 	}
@@ -295,7 +295,7 @@ static int usbfs_mkdir (struct inode *di
 	mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
 	res = usbfs_mknod (dir, dentry, mode, 0);
 	if (!res)
-		dir->i_nlink++;
+		inc_nlink(dir);
 	return res;
 }
 
fs/9p/vfs_inode.c renamed from fs/9p/vfs_inode.c~B4-monitor_inc_nlink +1 -1
diff -puN fs/9p/vfs_inode.c~B4-monitor_inc_nlink fs/9p/vfs_inode.c
@@ -233,7 +233,7 @@ struct inode *v9fs_get_inode(struct supe
 			inode->i_op = &v9fs_symlink_inode_operations;
 			break;
 		case S_IFDIR:
-			inode->i_nlink++;
+			inc_nlink(inode);
 			if(v9ses->extended)
 				inode->i_op = &v9fs_dir_inode_operations_ext;
 			else
fs/autofs/root.c renamed from fs/autofs/root.c~B4-monitor_inc_nlink +1 -1
diff -puN fs/autofs/root.c~B4-monitor_inc_nlink fs/autofs/root.c
@@ -466,7 +466,7 @@ static int autofs_root_mkdir(struct inod
 	ent->dentry = dentry;
 	autofs_hash_insert(dh,ent);
 
-	dir->i_nlink++;
+	inc_nlink(dir);
 	d_instantiate(dentry, iget(dir->i_sb,ino));
 	unlock_kernel();
 
fs/autofs4/root.c renamed from fs/autofs4/root.c~B4-monitor_inc_nlink +1 -1
diff -puN fs/autofs4/root.c~B4-monitor_inc_nlink fs/autofs4/root.c
@@ -713,7 +713,7 @@ static int autofs4_dir_mkdir(struct inod
 	if (p_ino && dentry->d_parent != dentry)
 		atomic_inc(&p_ino->count);
 	ino->inode = inode;
-	dir->i_nlink++;
+	inc_nlink(dir);
 	dir->i_mtime = CURRENT_TIME;
 
 	return 0;
fs/bfs/dir.c renamed from fs/bfs/dir.c~B4-monitor_inc_nlink +1 -1
diff -puN fs/bfs/dir.c~B4-monitor_inc_nlink fs/bfs/dir.c
@@ -163,7 +163,7 @@ static int bfs_link(struct dentry * old,
 		unlock_kernel();
 		return err;
 	}
-	inode->i_nlink++;
+	inc_nlink(inode);
 	inode->i_ctime = CURRENT_TIME_SEC;
 	mark_inode_dirty(inode);
 	atomic_inc(&inode->i_count);
fs/cifs/inode.c renamed from fs/cifs/inode.c~B4-monitor_inc_nlink +1 -1
diff -puN fs/cifs/inode.c~B4-monitor_inc_nlink fs/cifs/inode.c
@@ -736,7 +736,7 @@ int cifs_mkdir(struct inode *inode, stru
 		cFYI(1, ("cifs_mkdir returned 0x%x", rc));
 		d_drop(direntry);
 	} else {
-		inode->i_nlink++;
+		inc_nlink(inode);
 		if (pTcon->ses->capabilities & CAP_UNIX)
 			rc = cifs_get_inode_info_unix(&newinode, full_path,
 						      inode->i_sb,xid);
fs/coda/dir.c renamed from fs/coda/dir.c~B4-monitor_inc_nlink +1 -1
diff -puN fs/coda/dir.c~B4-monitor_inc_nlink fs/coda/dir.c
@@ -304,7 +304,7 @@ static int coda_link(struct dentry *sour
 	coda_dir_changed(dir_inode, 0);
 	atomic_inc(&inode->i_count);
 	d_instantiate(de, inode);
-	inode->i_nlink++;
+	inc_nlink(inode);
         
 out:
 	unlock_kernel();
fs/configfs/dir.c renamed from fs/configfs/dir.c~B4-monitor_inc_nlink +2 -2
diff -puN fs/configfs/dir.c~B4-monitor_inc_nlink fs/configfs/dir.c
@@ -113,7 +113,7 @@ static int init_dir(struct inode * inode
 	inode->i_fop = &configfs_dir_operations;
 
 	/* directory inodes start off with i_nlink == 2 (for "." entry) */
-	inode->i_nlink++;
+	inc_nlink(inode);
 	return 0;
 }
 
@@ -141,7 +141,7 @@ static int create_dir(struct config_item
 	if (!error) {
 		error = configfs_create(d, mode, init_dir);
 		if (!error) {
-			p->d_inode->i_nlink++;
+			inc_nlink(p->d_inode);
 			(d)->d_op = &configfs_dentry_ops;
 		} else {
 			struct configfs_dirent *sd = d->d_fsdata;
fs/configfs/mount.c renamed from fs/configfs/mount.c~B4-monitor_inc_nlink +1 -1
diff -puN fs/configfs/mount.c~B4-monitor_inc_nlink fs/configfs/mount.c
@@ -84,7 +84,7 @@ static int configfs_fill_super(struct su
 		inode->i_op = &configfs_dir_inode_operations;
 		inode->i_fop = &configfs_dir_operations;
 		/* directory inodes start off with i_nlink == 2 (for "." entry) */
-		inode->i_nlink++;
+		inc_nlink(inode);
 	} else {
 		pr_debug("configfs: could not get root inode\n");
 		return -ENOMEM;
fs/debugfs/inode.c renamed from fs/debugfs/inode.c~B4-monitor_inc_nlink +2 -2
diff -puN fs/debugfs/inode.c~B4-monitor_inc_nlink fs/debugfs/inode.c
@@ -54,7 +54,7 @@ static struct inode *debugfs_get_inode(s
 			inode->i_fop = &simple_dir_operations;
 
 			/* directory inodes start off with i_nlink == 2 (for "." entry) */
-			inode->i_nlink++;
+			inc_nlink(inode);
 			break;
 		}
 	}
@@ -87,7 +87,7 @@ static int debugfs_mkdir(struct inode *d
 	mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
 	res = debugfs_mknod(dir, dentry, mode, 0);
 	if (!res)
-		dir->i_nlink++;
+		inc_nlink(dir);
 	return res;
 }
 
fs/ext3/namei.c renamed from fs/ext3/namei.c~B4-monitor_inc_nlink +3 -3
diff -puN fs/ext3/namei.c~B4-monitor_inc_nlink fs/ext3/namei.c
@@ -1616,7 +1616,7 @@ static int ext3_delete_entry (handle_t *
  */
 static inline void ext3_inc_count(handle_t *handle, struct inode *inode)
 {
-	inode->i_nlink++;
+	inc_nlink(inode);
 }
 
 static inline void ext3_dec_count(handle_t *handle, struct inode *inode)
@@ -1775,7 +1775,7 @@ retry:
 		iput (inode);
 		goto out_stop;
 	}
-	dir->i_nlink++;
+	inc_nlink(dir);
 	ext3_update_dx_flag(dir);
 	ext3_mark_inode_dirty(handle, dir);
 	d_instantiate(dentry, inode);
@@ -2341,7 +2341,7 @@ static int ext3_rename (struct inode * o
 		if (new_inode) {
 			drop_nlink(new_inode);
 		} else {
-			new_dir->i_nlink++;
+			inc_nlink(new_dir);
 			ext3_update_dx_flag(new_dir);
 			ext3_mark_inode_dirty(handle, new_dir);
 		}
fs/fuse/control.c renamed from fs/fuse/control.c~B4-monitor_inc_nlink +1 -1
diff -puN fs/fuse/control.c~B4-monitor_inc_nlink fs/fuse/control.c
@@ -116,7 +116,7 @@ int fuse_ctl_add_conn(struct fuse_conn *
 		return 0;
 
 	parent = fuse_control_sb->s_root;
-	parent->d_inode->i_nlink++;
+	inc_nlink(parent->d_inode);
 	sprintf(name, "%llu", (unsigned long long) fc->id);
 	parent = fuse_ctl_add_dentry(parent, fc, name, S_IFDIR | 0500, 2,
 				     &simple_dir_inode_operations,
fs/hfsplus/dir.c renamed from fs/hfsplus/dir.c~B4-monitor_inc_nlink +1 -1
diff -puN fs/hfsplus/dir.c~B4-monitor_inc_nlink fs/hfsplus/dir.c
@@ -298,7 +298,7 @@ static int hfsplus_link(struct dentry *s
 	if (res)
 		return res;
 
-	inode->i_nlink++;
+	inc_nlink(inode);
 	hfsplus_instantiate(dst_dentry, inode, cnid);
 	atomic_inc(&inode->i_count);
 	inode->i_ctime = CURRENT_TIME_SEC;
fs/hpfs/namei.c renamed from fs/hpfs/namei.c~B4-monitor_inc_nlink +2 -2
diff -puN fs/hpfs/namei.c~B4-monitor_inc_nlink fs/hpfs/namei.c
@@ -89,7 +89,7 @@ static int hpfs_mkdir(struct inode *dir,
 	brelse(bh);
 	hpfs_mark_4buffers_dirty(&qbh0);
 	hpfs_brelse4(&qbh0);
-	dir->i_nlink++;
+	inc_nlink(dir);
 	insert_inode_hash(result);
 
 	if (result->i_uid != current->fsuid ||
@@ -635,7 +635,7 @@ static int hpfs_rename(struct inode *old
 	end:
 	hpfs_i(i)->i_parent_dir = new_dir->i_ino;
 	if (S_ISDIR(i->i_mode)) {
-		new_dir->i_nlink++;
+		inc_nlink(new_dir);
 		drop_nlink(old_dir);
 	}
 	if ((fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
fs/hugetlbfs/inode.c renamed from fs/hugetlbfs/inode.c~B4-monitor_inc_nlink +2 -2
diff -puN fs/hugetlbfs/inode.c~B4-monitor_inc_nlink fs/hugetlbfs/inode.c
@@ -377,7 +377,7 @@ static struct inode *hugetlbfs_get_inode
 			inode->i_fop = &simple_dir_operations;
 
 			/* directory inodes start off with i_nlink == 2 (for "." entry) */
-			inode->i_nlink++;
+			inc_nlink(inode);
 			break;
 		case S_IFLNK:
 			inode->i_op = &page_symlink_inode_operations;
@@ -418,7 +418,7 @@ static int hugetlbfs_mkdir(struct inode 
 {
 	int retval = hugetlbfs_mknod(dir, dentry, mode | S_IFDIR, 0);
 	if (!retval)
-		dir->i_nlink++;
+		inc_nlink(dir);
 	return retval;
 }
 
fs/jffs2/dir.c renamed from fs/jffs2/dir.c~B4-monitor_inc_nlink +3 -3
diff -puN fs/jffs2/dir.c~B4-monitor_inc_nlink fs/jffs2/dir.c
@@ -588,7 +588,7 @@ static int jffs2_mkdir (struct inode *di
 	}
 
 	dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
-	dir_i->i_nlink++;
+	inc_nlink(dir_i);
 
 	jffs2_free_raw_dirent(rd);
 
@@ -836,7 +836,7 @@ static int jffs2_rename (struct inode *o
 	/* If it was a directory we moved, and there was no victim,
 	   increase i_nlink on its new parent */
 	if (S_ISDIR(old_dentry->d_inode->i_mode) && !victim_f)
-		new_dir_i->i_nlink++;
+		inc_nlink(new_dir_i);
 
 	/* Unlink the original */
 	ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
@@ -848,7 +848,7 @@ static int jffs2_rename (struct inode *o
 		/* Oh shit. We really ought to make a single node which can do both atomically */
 		struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
 		down(&f->sem);
-		old_dentry->d_inode->i_nlink++;
+		inc_nlink(old_dentry->d_inode);
 		if (f->inocache)
 			f->inocache->nlink++;
 		up(&f->sem);
fs/jffs2/fs.c renamed from fs/jffs2/fs.c~B4-monitor_inc_nlink +3 -3
diff -puN fs/jffs2/fs.c~B4-monitor_inc_nlink fs/jffs2/fs.c
@@ -277,13 +277,13 @@ void jffs2_read_inode (struct inode *ino
 
 		for (fd=f->dents; fd; fd = fd->next) {
 			if (fd->type == DT_DIR && fd->ino)
-				inode->i_nlink++;
+				inc_nlink(inode);
 		}
 		/* and '..' */
-		inode->i_nlink++;
+		inc_nlink(inode);
 		/* Root dir gets i_nlink 3 for some reason */
 		if (inode->i_ino == 1)
-			inode->i_nlink++;
+			inc_nlink(inode);
 
 		inode->i_op = &jffs2_dir_inode_operations;
 		inode->i_fop = &jffs2_dir_operations;
fs/jfs/namei.c renamed from fs/jfs/namei.c~B4-monitor_inc_nlink +3 -3
diff -puN fs/jfs/namei.c~B4-monitor_inc_nlink fs/jfs/namei.c
@@ -292,7 +292,7 @@ static int jfs_mkdir(struct inode *dip, 
 	mark_inode_dirty(ip);
 
 	/* update parent directory inode */
-	dip->i_nlink++;		/* for '..' from child directory */
+	inc_nlink(dip);		/* for '..' from child directory */
 	dip->i_ctime = dip->i_mtime = CURRENT_TIME;
 	mark_inode_dirty(dip);
 
@@ -822,7 +822,7 @@ static int jfs_link(struct dentry *old_d
 		goto free_dname;
 
 	/* update object inode */
-	ip->i_nlink++;		/* for new link */
+	inc_nlink(ip);		/* for new link */
 	ip->i_ctime = CURRENT_TIME;
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	mark_inode_dirty(dir);
@@ -1206,7 +1206,7 @@ static int jfs_rename(struct inode *old_
 			goto out4;
 		}
 		if (S_ISDIR(old_ip->i_mode))
-			new_dir->i_nlink++;
+			inc_nlink(new_dir);
 	}
 	/*
 	 * Remove old directory entry
fs/msdos/namei.c renamed from fs/msdos/namei.c~B4-monitor_inc_nlink +2 -2
diff -puN fs/msdos/namei.c~B4-monitor_inc_nlink fs/msdos/namei.c
@@ -385,7 +385,7 @@ static int msdos_mkdir(struct inode *dir
 	err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo);
 	if (err)
 		goto out_free;
-	dir->i_nlink++;
+	inc_nlink(dir);
 
 	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
 	brelse(sinfo.bh);
@@ -544,7 +544,7 @@ static int do_msdos_rename(struct inode 
 		}
 		drop_nlink(old_dir);
 		if (!new_inode)
-			new_dir->i_nlink++;
+			inc_nlink(new_dir);
 	}
 
 	err = fat_remove_entries(old_dir, &old_sinfo);	/* and releases bh */
fs/ocfs2/dlm/dlmfs.c renamed from fs/ocfs2/dlm/dlmfs.c~B4-monitor_inc_nlink +3 -3
diff -puN fs/ocfs2/dlm/dlmfs.c~B4-monitor_inc_nlink fs/ocfs2/dlm/dlmfs.c
@@ -338,7 +338,7 @@ static struct inode *dlmfs_get_root_inod
 		inode->i_blocks = 0;
 		inode->i_mapping->backing_dev_info = &dlmfs_backing_dev_info;
 		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-		inode->i_nlink++;
+		inc_nlink(inode);
 
 		inode->i_fop = &simple_dir_operations;
 		inode->i_op = &dlmfs_root_inode_operations;
@@ -395,7 +395,7 @@ static struct inode *dlmfs_get_inode(str
 
 		/* directory inodes start off with i_nlink ==
 		 * 2 (for "." entry) */
-		inode->i_nlink++;
+		inc_nlink(inode);
 		break;
 	}
 
@@ -449,7 +449,7 @@ static int dlmfs_mkdir(struct inode * di
 	}
 	ip->ip_dlm = dlm;
 
-	dir->i_nlink++;
+	inc_nlink(dir);
 	d_instantiate(dentry, inode);
 	dget(dentry);	/* Extra count - pin the dentry in core */
 
fs/ocfs2/namei.c renamed from fs/ocfs2/namei.c~B4-monitor_inc_nlink +4 -4
diff -puN fs/ocfs2/namei.c~B4-monitor_inc_nlink fs/ocfs2/namei.c
@@ -408,7 +408,7 @@ static int ocfs2_mknod(struct inode *dir
 			mlog_errno(status);
 			goto leave;
 		}
-		dir->i_nlink++;
+		inc_nlink(dir);
 	}
 
 	status = ocfs2_add_entry(handle, dentry, inode,
@@ -702,7 +702,7 @@ static int ocfs2_link(struct dentry *old
 		goto bail;
 	}
 
-	inode->i_nlink++;
+	inc_nlink(inode);
 	inode->i_ctime = CURRENT_TIME;
 	fe->i_links_count = cpu_to_le16(inode->i_nlink);
 	fe->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
@@ -902,7 +902,7 @@ static int ocfs2_unlink(struct inode *di
 						parent_node_bh);
 		if (status < 0) {
 			mlog_errno(status);
-			dir->i_nlink++;
+			inc_nlink(dir);
 		}
 	}
 
@@ -1344,7 +1344,7 @@ static int ocfs2_rename(struct inode *ol
 		if (new_inode) {
 			new_inode->i_nlink--;
 		} else {
-			new_dir->i_nlink++;
+			inc_nlink(new_dir);
 			mark_inode_dirty(new_dir);
 		}
 	}
fs/ramfs/inode.c renamed from fs/ramfs/inode.c~B4-monitor_inc_nlink +2 -2
diff -puN fs/ramfs/inode.c~B4-monitor_inc_nlink fs/ramfs/inode.c
@@ -75,7 +75,7 @@ struct inode *ramfs_get_inode(struct sup
 			inode->i_fop = &simple_dir_operations;
 
 			/* directory inodes start off with i_nlink == 2 (for "." entry) */
-			inode->i_nlink++;
+			inc_nlink(inode);
 			break;
 		case S_IFLNK:
 			inode->i_op = &page_symlink_inode_operations;
@@ -113,7 +113,7 @@ static int ramfs_mkdir(struct inode * di
 {
 	int retval = ramfs_mknod(dir, dentry, mode | S_IFDIR, 0);
 	if (!retval)
-		dir->i_nlink++;
+		inc_nlink(dir);
 	return retval;
 }
 
fs/reiserfs/namei.c renamed from fs/reiserfs/namei.c~B4-monitor_inc_nlink +3 -3
diff -puN fs/reiserfs/namei.c~B4-monitor_inc_nlink fs/reiserfs/namei.c
@@ -19,7 +19,7 @@
 #include <linux/smp_lock.h>
 #include <linux/quotaops.h>
 
-#define INC_DIR_INODE_NLINK(i) if (i->i_nlink != 1) { i->i_nlink++; if (i->i_nlink >= REISERFS_LINK_MAX) i->i_nlink=1; }
+#define INC_DIR_INODE_NLINK(i) if (i->i_nlink != 1) { inc_nlink(i); if (i->i_nlink >= REISERFS_LINK_MAX) i->i_nlink=1; }
 #define DEC_DIR_INODE_NLINK(i) if (i->i_nlink != 1) drop_nlink(i);
 
 // directory item contains array of entry headers. This performs
@@ -1006,7 +1006,7 @@ static int reiserfs_unlink(struct inode 
 	    reiserfs_cut_from_item(&th, &path, &(de.de_entry_key), dir, NULL,
 				   0);
 	if (retval < 0) {
-		inode->i_nlink++;
+		inc_nlink(inode);
 		goto end_unlink;
 	}
 	inode->i_ctime = CURRENT_TIME_SEC;
@@ -1143,7 +1143,7 @@ static int reiserfs_link(struct dentry *
 	}
 
 	/* inc before scheduling so reiserfs_unlink knows we are here */
-	inode->i_nlink++;
+	inc_nlink(inode);
 
 	retval = journal_begin(&th, dir->i_sb, jbegin_count);
 	if (retval) {
fs/sysfs/dir.c renamed from fs/sysfs/dir.c~B4-monitor_inc_nlink +2 -2
diff -puN fs/sysfs/dir.c~B4-monitor_inc_nlink fs/sysfs/dir.c
@@ -103,7 +103,7 @@ static int init_dir(struct inode * inode
 	inode->i_fop = &sysfs_dir_operations;
 
 	/* directory inodes start off with i_nlink == 2 (for "." entry) */
-	inode->i_nlink++;
+	inc_nlink(inode);
 	return 0;
 }
 
@@ -137,7 +137,7 @@ static int create_dir(struct kobject * k
 		if (!error) {
 			error = sysfs_create(*d, mode, init_dir);
 			if (!error) {
-				p->d_inode->i_nlink++;
+				inc_nlink(p->d_inode);
 				(*d)->d_op = &sysfs_dentry_ops;
 				d_rehash(*d);
 			}
fs/sysfs/mount.c renamed from fs/sysfs/mount.c~B4-monitor_inc_nlink +1 -1
diff -puN fs/sysfs/mount.c~B4-monitor_inc_nlink fs/sysfs/mount.c
@@ -49,7 +49,7 @@ static int sysfs_fill_super(struct super
 		inode->i_op = &sysfs_dir_inode_operations;
 		inode->i_fop = &sysfs_dir_operations;
 		/* directory inodes start off with i_nlink == 2 (for "." entry) */
-		inode->i_nlink++;	
+		inc_nlink(inode);
 	} else {
 		pr_debug("sysfs: could not get root inode\n");
 		return -ENOMEM;
fs/udf/inode.c renamed from fs/udf/inode.c~B4-monitor_inc_nlink +1 -1
diff -puN fs/udf/inode.c~B4-monitor_inc_nlink fs/udf/inode.c
@@ -1165,7 +1165,7 @@ static void udf_fill_inode(struct inode 
 			inode->i_op = &udf_dir_inode_operations;
 			inode->i_fop = &udf_dir_operations;
 			inode->i_mode |= S_IFDIR;
-			inode->i_nlink ++;
+			inc_nlink(inode);
 			break;
 		}
 		case ICBTAG_FILE_TYPE_REALTIME:
fs/udf/namei.c renamed from fs/udf/namei.c~B4-monitor_inc_nlink +3 -3
diff -puN fs/udf/namei.c~B4-monitor_inc_nlink fs/udf/namei.c
@@ -762,7 +762,7 @@ static int udf_mkdir(struct inode * dir,
 		cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL);
 	cfi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY;
 	udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
-	dir->i_nlink++;
+	inc_nlink(dir);
 	mark_inode_dirty(dir);
 	d_instantiate(dentry, inode);
 	if (fibh.sbh != fibh.ebh)
@@ -1147,7 +1147,7 @@ static int udf_link(struct dentry * old_
 	if (fibh.sbh != fibh.ebh)
 		udf_release_data(fibh.ebh);
 	udf_release_data(fibh.sbh);
-	inode->i_nlink ++;
+	inc_nlink(inode);
 	inode->i_ctime = current_fs_time(inode->i_sb);
 	mark_inode_dirty(inode);
 	atomic_inc(&inode->i_count);
@@ -1282,7 +1282,7 @@ static int udf_rename (struct inode * ol
 		}
 		else
 		{
-			new_dir->i_nlink ++;
+			inc_nlink(new_dir);
 			mark_inode_dirty(new_dir);
 		}
 	}
fs/vfat/namei.c renamed from fs/vfat/namei.c~B4-monitor_inc_nlink +2 -2
diff -puN fs/vfat/namei.c~B4-monitor_inc_nlink fs/vfat/namei.c
@@ -837,7 +837,7 @@ static int vfat_mkdir(struct inode *dir,
 	if (err)
 		goto out_free;
 	dir->i_version++;
-	dir->i_nlink++;
+	inc_nlink(dir);
 
 	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
 	brelse(sinfo.bh);
@@ -932,7 +932,7 @@ static int vfat_rename(struct inode *old
 		}
 		drop_nlink(old_dir);
 		if (!new_inode)
- 			new_dir->i_nlink++;
+ 			inc_nlink(new_dir);
 	}
 
 	err = fat_remove_entries(old_dir, &old_sinfo);	/* and releases bh */
ipc/mqueue.c renamed from ipc/mqueue.c~B4-monitor_inc_nlink +1 -1
diff -puN ipc/mqueue.c~B4-monitor_inc_nlink ipc/mqueue.c
@@ -168,7 +168,7 @@ static struct inode *mqueue_get_inode(st
 			/* all is ok */
 			info->user = get_uid(u);
 		} else if (S_ISDIR(mode)) {
-			inode->i_nlink++;
+			inc_nlink(inode);
 			/* Some things misbehave if size == 0 on a directory */
 			inode->i_size = 2 * DIRENT_SIZE;
 			inode->i_op = &mqueue_dir_inode_operations;
kernel/cpuset.c renamed from kernel/cpuset.c~B4-monitor_inc_nlink +4 -4
diff -puN kernel/cpuset.c~B4-monitor_inc_nlink kernel/cpuset.c
@@ -377,7 +377,7 @@ static int cpuset_fill_super(struct supe
 		inode->i_op = &simple_dir_inode_operations;
 		inode->i_fop = &simple_dir_operations;
 		/* directories start off with i_nlink == 2 (for "." entry) */
-		inode->i_nlink++;
+		inc_nlink(inode);
 	} else {
 		return -ENOMEM;
 	}
@@ -1552,7 +1552,7 @@ static int cpuset_create_file(struct den
 		inode->i_fop = &simple_dir_operations;
 
 		/* start off with i_nlink == 2 (for "." entry) */
-		inode->i_nlink++;
+		inc_nlink(inode);
 	} else if (S_ISREG(mode)) {
 		inode->i_size = 0;
 		inode->i_fop = &cpuset_file_operations;
@@ -1585,7 +1585,7 @@ static int cpuset_create_dir(struct cpus
 	error = cpuset_create_file(dentry, S_IFDIR | mode);
 	if (!error) {
 		dentry->d_fsdata = cs;
-		parent->d_inode->i_nlink++;
+		inc_nlink(parent->d_inode);
 		cs->dentry = dentry;
 	}
 	dput(dentry);
@@ -2020,7 +2020,7 @@ int __init cpuset_init(void)
 	}
 	root = cpuset_mount->mnt_sb->s_root;
 	root->d_fsdata = &top_cpuset;
-	root->d_inode->i_nlink++;
+	inc_nlink(root->d_inode);
 	top_cpuset.dentry = root;
 	root->d_inode->i_op = &cpuset_dir_inode_operations;
 	number_of_cpusets = 1;
net/sunrpc/rpc_pipe.c renamed from net/sunrpc/rpc_pipe.c~B4-monitor_inc_nlink +3 -3
diff -puN net/sunrpc/rpc_pipe.c~B4-monitor_inc_nlink net/sunrpc/rpc_pipe.c
@@ -496,7 +496,7 @@ rpc_get_inode(struct super_block *sb, in
 		case S_IFDIR:
 			inode->i_fop = &simple_dir_operations;
 			inode->i_op = &simple_dir_inode_operations;
-			inode->i_nlink++;
+			inc_nlink(inode);
 		default:
 			break;
 	}
@@ -572,7 +572,7 @@ rpc_populate(struct dentry *parent,
 		if (private)
 			rpc_inode_setowner(inode, private);
 		if (S_ISDIR(mode))
-			dir->i_nlink++;
+			inc_nlink(dir);
 		d_add(dentry, inode);
 	}
 	mutex_unlock(&dir->i_mutex);
@@ -594,7 +594,7 @@ __rpc_mkdir(struct inode *dir, struct de
 		goto out_err;
 	inode->i_ino = iunique(dir->i_sb, 100);
 	d_instantiate(dentry, inode);
-	dir->i_nlink++;
+	inc_nlink(dir);
 	inode_dir_notify(dir, DN_CREATE);
 	return 0;
 out_err:
security/inode.c renamed from security/inode.c~B4-monitor_inc_nlink +2 -2
diff -puN security/inode.c~B4-monitor_inc_nlink security/inode.c
@@ -78,7 +78,7 @@ static struct inode *get_inode(struct su
 			inode->i_fop = &simple_dir_operations;
 
 			/* directory inodes start off with i_nlink == 2 (for "." entry) */
-			inode->i_nlink++;
+			inc_nlink(inode);
 			break;
 		}
 	}
@@ -111,7 +111,7 @@ static int mkdir(struct inode *dir, stru
 	mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
 	res = mknod(dir, dentry, mode, 0);
 	if (!res)
-		dir->i_nlink++;
+		inc_nlink(dir);
 	return res;
 }
 
security/selinux/selinuxfs.c renamed from security/selinux/selinuxfs.c~B4-monitor_inc_nlink +2 -2
diff -puN security/selinux/selinuxfs.c~B4-monitor_inc_nlink security/selinux/selinuxfs.c
@@ -1253,10 +1253,10 @@ static int sel_make_dir(struct inode *di
 	inode->i_op = &simple_dir_inode_operations;
 	inode->i_fop = &simple_dir_operations;
 	/* directory inodes start off with i_nlink == 2 (for "." entry) */
-	inode->i_nlink++;
+	inc_nlink(inode);
 	d_add(dentry, inode);
 	/* bump link count on parent directory, too */
-	dir->i_nlink++;
+	inc_nlink(dir);
 out:
 	return ret;
 }

_

Acked-by on v1 4/6
#12 lore raw

Re: [PATCH 4/6] r/o bind mount prepwork: inc_nlink() helper

Quoted 4 lines · Dave Hansen

This is mostly included for parity with dec_nlink(), where we will have some more hooks. This one should stay pretty darn straightforward for now.

#13 lore raw

PATCH 5/6 clean up OCFS2 nlink handling

In reply to Dave Hansen

OCFS2 does some operations on i_nlink, then reverts them if some of its operations fail to complete. This does not fit in well with the drop_nlink() logic where we expect i_nlink to stay at zero once it gets there.

So, delay all of the nlink operations until we're sure that the operations have completed. Also, introduce a small helper to check whether an inode has proper "unlinkable" i_nlink counts no matter whether it is a directory or regular inode.

This patch is broken out from the others because it does contain some logical changes.

---

 arch/i386/kernel/setup.c  |    0 
 lxc-dave/fs/ocfs2/namei.c |   45 ++++++++++++++++++++++++---------------------
 2 files changed, 24 insertions(+), 21 deletions(-)
fs/ocfs2/namei.c renamed from fs/ocfs2/namei.c~B5-clean_up_OCFS2_nlink_handling +24 -21
diff -puN fs/ocfs2/namei.c~B5-clean_up_OCFS2_nlink_handling fs/ocfs2/namei.c
@@ -744,11 +744,23 @@ bail:
 	return err;
 }
 
+static inline int inode_is_unlinkable(struct inode *inode)
+{
+	if (S_ISDIR(inode->i_mode)) {
+	       	if (inode->i_nlink == 2)
+			return 1;
+		return 0;
+	}
+
+	if (inode->i_nlink == 1)
+		return 1;
+	return 0;
+}
+
 static int ocfs2_unlink(struct inode *dir,
 			struct dentry *dentry)
 {
 	int status;
-	unsigned int saved_nlink = 0;
 	struct inode *inode = dentry->d_inode;
 	struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
 	u64 blkno;
@@ -760,6 +772,7 @@ static int ocfs2_unlink(struct inode *di
 	struct buffer_head *dirent_bh = NULL;
 	char orphan_name[OCFS2_ORPHAN_NAMELEN + 1];
 	struct buffer_head *orphan_entry_bh = NULL;
+	unsigned int future_nlink;
 
 	mlog_entry("(0x%p, 0x%p, '%.*s')\n", dir, dentry,
 		   dentry->d_name.len, dentry->d_name.name);
@@ -823,18 +836,11 @@ static int ocfs2_unlink(struct inode *di
 		}
 	}
 
-	/* There are still a few steps left until we can consider the
-	 * unlink to have succeeded. Save off nlink here before
-	 * modification so we can set it back in case we hit an issue
-	 * before commit. */
-	saved_nlink = inode->i_nlink;
-	if (S_ISDIR(inode->i_mode))
-		inode->i_nlink = 0;
+	if (S_ISDIR(inode->i_mode) && (inode->i_nlink == 2))
+		future_nlink = 0;
 	else
-		inode->i_nlink--;
-
-	status = ocfs2_request_unlink_vote(inode, dentry,
-					   (unsigned int) inode->i_nlink);
+		future_nlink = inode->i_nlink - 1;
+	status = ocfs2_request_unlink_vote(inode, dentry, future_nlink);
 	if (status < 0) {
 		/* This vote should succeed under all normal
 		 * circumstances. */
@@ -842,7 +848,7 @@ static int ocfs2_unlink(struct inode *di
 		goto leave;
 	}
 
-	if (!inode->i_nlink) {
+	if (inode_is_unlinkable(inode)) {
 		status = ocfs2_prepare_orphan_dir(osb, handle, inode,
 						  orphan_name,
 						  &orphan_entry_bh);
@@ -869,7 +875,7 @@ static int ocfs2_unlink(struct inode *di
 
 	fe = (struct ocfs2_dinode *) fe_bh->b_data;
 
-	if (!inode->i_nlink) {
+	if (inode_is_unlinkable(inode)) {
 		status = ocfs2_orphan_add(osb, handle, inode, fe, orphan_name,
 					  orphan_entry_bh);
 		if (status < 0) {
@@ -885,10 +891,10 @@ static int ocfs2_unlink(struct inode *di
 		goto leave;
 	}
 
-	/* We can set nlink on the dinode now. clear the saved version
-	 * so that it doesn't get set later. */
+	if (S_ISDIR(inode->i_mode))
+		drop_nlink(inode);
+	drop_nlink(inode);
 	fe->i_links_count = cpu_to_le16(inode->i_nlink);
-	saved_nlink = 0;
 
 	status = ocfs2_journal_dirty(handle, fe_bh);
 	if (status < 0) {
@@ -897,7 +903,7 @@ static int ocfs2_unlink(struct inode *di
 	}
 
 	if (S_ISDIR(inode->i_mode)) {
-		dir->i_nlink--;
+		drop_nlink(dir);
 		status = ocfs2_mark_inode_dirty(handle, dir,
 						parent_node_bh);
 		if (status < 0) {
@@ -907,9 +913,6 @@ static int ocfs2_unlink(struct inode *di
 	}
 
 leave:
-	if (status < 0 && saved_nlink)
-		inode->i_nlink = saved_nlink;
-
 	if (handle)
 		ocfs2_commit_trans(handle);
 

diff -puN arch/i386/kernel/setup.c~B5-clean_up_OCFS2_nlink_handling arch/i386/kernel/setup.c _

#14 lore raw

Re: [PATCH 5/6] clean up OCFS2 nlink handling

Quoted 6 lines · Dave Hansen

OCFS2 does some operations on i_nlink, then reverts them if some of its operations fail to complete. This does not fit in well with the drop_nlink() logic where we expect i_nlink to stay at zero once it gets there.

Quoted 7 lines

So, delay all of the nlink operations until we're sure that the operations have completed. Also, introduce a small helper to check whether an inode has proper "unlinkable" i_nlink counts no matter whether it is a directory or regular inode.

This patch is broken out from the others because it does contain some logical changes.

looks good to me, although I probably can't ACK ocfs2 patches. did you look whether gfs2 in -mm needs something similar?

#15 lore raw

Re: [PATCH 5/6] clean up OCFS2 nlink handling

Quoted 11 lines · Christoph Hellwig
Quoted 10 lines · Dave Hansen

OCFS2 does some operations on i_nlink, then reverts them if some of its operations fail to complete. This does not fit in well with the drop_nlink() logic where we expect i_nlink to stay at zero once it gets there.

So, delay all of the nlink operations until we're sure that the operations have completed. Also, introduce a small helper to check whether an inode has proper "unlinkable" i_nlink counts no matter whether it is a directory or regular inode.

Quoted 4 lines
Quoted 2 lines

This patch is broken out from the others because it does contain some logical changes.

looks good to me, although I probably can't ACK ocfs2 patches.

That's probably OK. One of the Oracle guys was nice enough to help me beat it into shape and sign off on it.

Quoted 1 line

did you look whether gfs2 in -mm needs something similar?

It doesn't appear to. It doesn't manipulate i_nlink in the same, direct manner.

-- Dave

#16 lore raw

Re: [PATCH 5/6] clean up OCFS2 nlink handling

Hi,

Quoted 1 line · Dave Hansen

On Wed, 2006-08-09 at 18:12 +0100, Christoph Hellwig wrote:

[snip]

Quoted 6 lines
Quoted 1 line

did you look whether gfs2 in -mm needs something similar?

It doesn't appear to. It doesn't manipulate i_nlink in the same, direct manner.

-- Dave

I think it will need something similar. I suspect the required changes will all be confined to routines in inode.c. If the link count is changed by (a) remote node(s), then gfs2_inode_attr_in() might change the link count. Also gfs2_change_nlink() is the other place to look. I think everywhere else is ok,

Steve.

#17 lore raw

Re: [PATCH 5/6] clean up OCFS2 nlink handling

Quoted 5 lines · Steven Whitehouse
Quoted 1 line · Dave Hansen

On Wed, 2006-08-09 at 18:12 +0100, Christoph Hellwig wrote:

[snip]

Quoted 2 lines
Quoted 1 line

did you look whether gfs2 in -mm needs something similar?

Quoted 8 lines
Quoted 2 lines

It doesn't appear to. It doesn't manipulate i_nlink in the same, direct manner.

I think it will need something similar. I suspect the required changes will all be confined to routines in inode.c. If the link count is changed by (a) remote node(s), then gfs2_inode_attr_in() might change the link count. Also gfs2_change_nlink() is the other place to look. I think everywhere else is ok,

Well, I think this is all that it needs. I'm trying to decide if we need a set_nlink() function for users like this, but I'm not sure there are enough of them.

---

 lxc-dave/fs/gfs2/inode.c |    3 +++
 2 files changed, 3 insertions(+)
fs/gfs2/inode.c renamed from fs/gfs2/inode.c~gfs +3 -0
diff -puN fs/gfs2/inode.c~gfs fs/gfs2/inode.c
@@ -332,6 +332,9 @@ int gfs2_change_nlink(struct gfs2_inode 
 	ip->i_di.di_ctime = get_seconds();
 	ip->i_inode.i_nlink = nlink;
 
+	if (!nlink)
+		ip->i_inode.i_state |= I_AWAITING_FINAL_IPUT;
+
 	gfs2_trans_add_bh(ip->i_gl, dibh, 1);
 	gfs2_dinode_out(&ip->i_di, dibh->b_data);
 	brelse(dibh);

-- Dave

#18 lore raw

Re: [PATCH 5/6] clean up OCFS2 nlink handling

Hi,

Quoted 15 lines · Dave Hansen
Quoted 5 lines · Steven Whitehouse
Quoted 1 line · Dave Hansen

On Wed, 2006-08-09 at 18:12 +0100, Christoph Hellwig wrote:

[snip]

Quoted 2 lines
Quoted 1 line

did you look whether gfs2 in -mm needs something similar?

Quoted 8 lines
Quoted 2 lines

It doesn't appear to. It doesn't manipulate i_nlink in the same, direct manner.

I think it will need something similar. I suspect the required changes will all be confined to routines in inode.c. If the link count is changed by (a) remote node(s), then gfs2_inode_attr_in() might change the link count. Also gfs2_change_nlink() is the other place to look. I think everywhere else is ok,

Quoted 4 lines

Well, I think this is all that it needs. I'm trying to decide if we need a set_nlink() function for users like this, but I'm not sure there are enough of them.

I suspect that something is required for gfs2_inode_attr_in() as well. Here is the scenario... suppose there are two nodes A and B. On node A a file is opened, on node B the same file is then unlinked such that its link count hits zero. If node A then does something to the file (read or write, it doesn't matter) then it will reread the attributes off disk via gfs2_inode_attr_in() and discover that the link count is then zero.

We don't play any games in that once the link count has hit zero, thats it, it will never increment above zero again. The node which actually deals with the deallocation and clean up is the one that performs the final close (iput). This is dealt with using the iopen lock to ensure that we are certain of which node is the last to close the file. The iopen lock is held in a shared state by all nodes having the file open. On gfs2_delete_inode we attempt to upgrade the iopen lock to exclusive, if this succeeds we then presume that no other node has the file open and proceed with deallocation, as per a normal local filesystem.

So if I've understood what you are doing correctly, the adjustment you've made below needs to be copied into gfs2_inode_attr_in() as well,

Steve.

Quoted 22 lines

---

 lxc-dave/fs/gfs2/inode.c |    3 +++
 2 files changed, 3 insertions(+)

diff -puN fs/gfs2/inode.c~gfs fs/gfs2/inode.c

fs/gfs2/inode.c renamed from fs/gfs2/inode.c~gfs +3 -1
@@ -332,6 +332,9 @@ int gfs2_change_nlink(struct gfs2_inode 
 	ip->i_di.di_ctime = get_seconds();
 	ip->i_inode.i_nlink = nlink;
 
+	if (!nlink)
+		ip->i_inode.i_state |= I_AWAITING_FINAL_IPUT;
+
 	gfs2_trans_add_bh(ip->i_gl, dibh, 1);
 	gfs2_dinode_out(&ip->i_di, dibh->b_data);
 	brelse(dibh);
 
 
-- Dave
#19 lore raw

PATCH 6/6 monitor zeroing of i_nlink

In reply to Dave Hansen

Some filesystems, instead of simply decrementing i_nlink, simply zero it during an unlink operation. We need to catch these in addition to the decrement operations.

---

 kernel/fork.c                |    0 
 lxc-dave/fs/autofs4/root.c   |    4 ++--
 lxc-dave/fs/cifs/inode.c     |    2 +-
 lxc-dave/fs/ext3/namei.c     |    2 +-
 lxc-dave/fs/fuse/dir.c       |    4 ++--
 lxc-dave/fs/hfs/dir.c        |    2 +-
 lxc-dave/fs/hfsplus/dir.c    |    4 ++--
 lxc-dave/fs/hpfs/namei.c     |    4 ++--
 lxc-dave/fs/jfs/namei.c      |    2 +-
 lxc-dave/fs/msdos/namei.c    |    4 ++--
 lxc-dave/fs/nfs/dir.c        |    2 +-
 lxc-dave/fs/qnx4/namei.c     |    2 +-
 lxc-dave/fs/reiserfs/namei.c |    4 ++--
 lxc-dave/fs/udf/namei.c      |    2 +-
 lxc-dave/fs/vfat/namei.c     |    4 ++--
 lxc-dave/include/linux/fs.h  |    5 +++++
 16 files changed, 26 insertions(+), 21 deletions(-)

diff -puN fs/libfs.c~B6-monitor-clear-i_nlink fs/libfs.c

fs/cifs/inode.c renamed from fs/cifs/inode.c~B6-monitor-clear-i_nlink +1 -1
diff -puN fs/cifs/inode.c~B6-monitor-clear-i_nlink fs/cifs/inode.c
@@ -819,7 +819,7 @@ int cifs_rmdir(struct inode *inode, stru
 	if (!rc) {
 		drop_nlink(inode);
 		i_size_write(direntry->d_inode,0);
-		direntry->d_inode->i_nlink = 0;
+		clear_nlink(direntry->d_inode);
 	}
 
 	cifsInode = CIFS_I(direntry->d_inode);
fs/ext3/namei.c renamed from fs/ext3/namei.c~B6-monitor-clear-i_nlink +1 -1
diff -puN fs/ext3/namei.c~B6-monitor-clear-i_nlink fs/ext3/namei.c
@@ -2045,7 +2045,7 @@ static int ext3_rmdir (struct inode * di
 			      "empty directory has nlink!=2 (%d)",
 			      inode->i_nlink);
 	inode->i_version++;
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	/* There's no need to set i_disksize: the fact that i_nlink is
 	 * zero will ensure that the right thing happens during any
 	 * recovery. */
fs/autofs4/root.c renamed from fs/autofs4/root.c~B6-monitor-clear-i_nlink +2 -2
diff -puN fs/autofs4/root.c~B6-monitor-clear-i_nlink fs/autofs4/root.c
@@ -638,7 +638,7 @@ static int autofs4_dir_unlink(struct ino
 	dput(ino->dentry);
 
 	dentry->d_inode->i_size = 0;
-	dentry->d_inode->i_nlink = 0;
+	clear_nlink(dentry->d_inode);
 
 	dir->i_mtime = CURRENT_TIME;
 
@@ -673,7 +673,7 @@ static int autofs4_dir_rmdir(struct inod
 	}
 	dput(ino->dentry);
 	dentry->d_inode->i_size = 0;
-	dentry->d_inode->i_nlink = 0;
+	clear_nlink(dentry->d_inode);
 
 	if (dir->i_nlink)
 		drop_nlink(dir);
fs/fuse/dir.c renamed from fs/fuse/dir.c~B6-monitor-clear-i_nlink +2 -2
diff -puN fs/fuse/dir.c~B6-monitor-clear-i_nlink fs/fuse/dir.c
@@ -508,7 +508,7 @@ static int fuse_unlink(struct inode *dir
 		/* Set nlink to zero so the inode can be cleared, if
                    the inode does have more links this will be
                    discovered at the next lookup/getattr */
-		inode->i_nlink = 0;
+		clear_nlink(inode);
 		fuse_invalidate_attr(inode);
 		fuse_invalidate_attr(dir);
 		fuse_invalidate_entry_cache(entry);
@@ -534,7 +534,7 @@ static int fuse_rmdir(struct inode *dir,
 	err = req->out.h.error;
 	fuse_put_request(fc, req);
 	if (!err) {
-		entry->d_inode->i_nlink = 0;
+		clear_nlink(entry->d_inode);
 		fuse_invalidate_attr(dir);
 		fuse_invalidate_entry_cache(entry);
 	} else if (err == -EINTR)
fs/hfs/dir.c renamed from fs/hfs/dir.c~B6-monitor-clear-i_nlink +1 -1
diff -puN fs/hfs/dir.c~B6-monitor-clear-i_nlink fs/hfs/dir.c
@@ -273,7 +273,7 @@ static int hfs_rmdir(struct inode *dir, 
 	res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name);
 	if (res)
 		return res;
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_ctime = CURRENT_TIME_SEC;
 	hfs_delete_inode(inode);
 	mark_inode_dirty(inode);
fs/hfsplus/dir.c renamed from fs/hfsplus/dir.c~B6-monitor-clear-i_nlink +2 -2
diff -puN fs/hfsplus/dir.c~B6-monitor-clear-i_nlink fs/hfsplus/dir.c
@@ -348,7 +348,7 @@ static int hfsplus_unlink(struct inode *
 		} else
 			inode->i_flags |= S_DEAD;
 	} else
-		inode->i_nlink = 0;
+		clear_nlink(inode);
 	inode->i_ctime = CURRENT_TIME_SEC;
 	mark_inode_dirty(inode);
 
@@ -387,7 +387,7 @@ static int hfsplus_rmdir(struct inode *d
 	res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
 	if (res)
 		return res;
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_ctime = CURRENT_TIME_SEC;
 	hfsplus_delete_inode(inode);
 	mark_inode_dirty(inode);
fs/hpfs/namei.c renamed from fs/hpfs/namei.c~B6-monitor-clear-i_nlink +2 -2
diff -puN fs/hpfs/namei.c~B6-monitor-clear-i_nlink fs/hpfs/namei.c
@@ -495,7 +495,7 @@ static int hpfs_rmdir(struct inode *dir,
 		break;
 	default:
 		drop_nlink(dir);
-		inode->i_nlink = 0;
+		clear_nlink(inode);
 		err = 0;
 	}
 	goto out;
@@ -590,7 +590,7 @@ static int hpfs_rename(struct inode *old
 		int r;
 		if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
 			if ((nde = map_dirent(new_dir, hpfs_i(new_dir)->i_dno, (char *)new_name, new_len, NULL, &qbh1))) {
-				new_inode->i_nlink = 0;
+				clear_nlink(new_inode);
 				copy_de(nde, &de);
 				memcpy(nde->name, new_name, new_len);
 				hpfs_mark_4buffers_dirty(&qbh1);
fs/jfs/namei.c renamed from fs/jfs/namei.c~B6-monitor-clear-i_nlink +1 -1
diff -puN fs/jfs/namei.c~B6-monitor-clear-i_nlink fs/jfs/namei.c
@@ -414,7 +414,7 @@ static int jfs_rmdir(struct inode *dip, 
 	JFS_IP(ip)->acl.flag = 0;
 
 	/* mark the target directory as deleted */
-	ip->i_nlink = 0;
+	clear_nlink(ip);
 	mark_inode_dirty(ip);
 
 	rc = txCommit(tid, 2, &iplist[0], 0);
fs/msdos/namei.c renamed from fs/msdos/namei.c~B6-monitor-clear-i_nlink +2 -2
diff -puN fs/msdos/namei.c~B6-monitor-clear-i_nlink fs/msdos/namei.c
@@ -343,7 +343,7 @@ static int msdos_rmdir(struct inode *dir
 		goto out;
 	drop_nlink(dir);
 
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_ctime = CURRENT_TIME_SEC;
 	fat_detach(inode);
 out:
@@ -425,7 +425,7 @@ static int msdos_unlink(struct inode *di
 	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
 	if (err)
 		goto out;
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_ctime = CURRENT_TIME_SEC;
 	fat_detach(inode);
 out:
fs/nfs/dir.c renamed from fs/nfs/dir.c~B6-monitor-clear-i_nlink +1 -1
diff -puN fs/nfs/dir.c~B6-monitor-clear-i_nlink fs/nfs/dir.c
@@ -1280,7 +1280,7 @@ static int nfs_rmdir(struct inode *dir, 
 	error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
 	/* Ensure the VFS deletes this inode */
 	if (error == 0 && dentry->d_inode != NULL)
-		dentry->d_inode->i_nlink = 0;
+		clear_nlink(dentry->d_inode);
 	nfs_end_data_update(dir);
 	unlock_kernel();
 

diff -puN fs/ocfs2/namei.c~B6-monitor-clear-i_nlink fs/ocfs2/namei.c

fs/qnx4/namei.c renamed from fs/qnx4/namei.c~B6-monitor-clear-i_nlink +1 -1
diff -puN fs/qnx4/namei.c~B6-monitor-clear-i_nlink fs/qnx4/namei.c
@@ -186,7 +186,7 @@ int qnx4_rmdir(struct inode *dir, struct
 	memset(de->di_fname, 0, sizeof de->di_fname);
 	de->di_mode = 0;
 	mark_buffer_dirty(bh);
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	mark_inode_dirty(inode);
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
 	inode_dec_link_count(dir);
fs/reiserfs/namei.c renamed from fs/reiserfs/namei.c~B6-monitor-clear-i_nlink +2 -2
diff -puN fs/reiserfs/namei.c~B6-monitor-clear-i_nlink fs/reiserfs/namei.c
@@ -913,7 +913,7 @@ static int reiserfs_rmdir(struct inode *
 		reiserfs_warning(inode->i_sb, "%s: empty directory has nlink "
 				 "!= 2 (%d)", __FUNCTION__, inode->i_nlink);
 
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
 	reiserfs_update_sd(&th, inode);
 
@@ -1473,7 +1473,7 @@ static int reiserfs_rename(struct inode 
 	if (new_dentry_inode) {
 		// adjust link number of the victim
 		if (S_ISDIR(new_dentry_inode->i_mode)) {
-			new_dentry_inode->i_nlink = 0;
+			clear_nlink(new_dentry_inode);
 		} else {
 			drop_nlink(new_dentry_inode);
 		}
fs/udf/namei.c renamed from fs/udf/namei.c~B6-monitor-clear-i_nlink +1 -1
diff -puN fs/udf/namei.c~B6-monitor-clear-i_nlink fs/udf/namei.c
@@ -876,7 +876,7 @@ static int udf_rmdir(struct inode * dir,
 		udf_warning(inode->i_sb, "udf_rmdir",
 			"empty directory has nlink != 2 (%d)",
 			inode->i_nlink);
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_size = 0;
 	inode_dec_link_count(inode);
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb);
fs/vfat/namei.c renamed from fs/vfat/namei.c~B6-monitor-clear-i_nlink +2 -2
diff -puN fs/vfat/namei.c~B6-monitor-clear-i_nlink fs/vfat/namei.c
@@ -784,7 +784,7 @@ static int vfat_rmdir(struct inode *dir,
 		goto out;
 	drop_nlink(dir);
 
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
 	fat_detach(inode);
 out:
@@ -808,7 +808,7 @@ static int vfat_unlink(struct inode *dir
 	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
 	if (err)
 		goto out;
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
 	fat_detach(inode);
 out:
include/linux/fs.h renamed from include/linux/fs.h~B6-monitor-clear-i_nlink +5 -0
diff -puN include/linux/fs.h~B6-monitor-clear-i_nlink include/linux/fs.h
@@ -1272,6 +1272,11 @@ static inline void drop_nlink(struct ino
 	inode->i_nlink--;
 }
 
+static inline void clear_nlink(struct inode *inode)
+{
+	inode->i_nlink = 0;
+}
+
 static inline void inode_dec_link_count(struct inode *inode)
 {
 	drop_nlink(inode);

diff -puN kernel/fork.c~B6-monitor-clear-i_nlink kernel/fork.c _

Acked-by on v1 6/6
#20 lore raw

Re: [PATCH 6/6] monitor zeroing of i_nlink

Quoted 6 lines · Dave Hansen

Some filesystems, instead of simply decrementing i_nlink, simply zero it during an unlink operation. We need to catch these in addition to the decrement operations.

btw, what about adding some nice kerneldoc for all the *_nlink helpers you added?

Quoted 3 lines

---

 kernel/fork.c                |    0 

something is screwed up in your diffstats :)