Skip to contentSkip to conversation

msync: massive code cleanup of sys_msync()

RFC PATCH In review Anton Salikhmetov 4 participants 18 messages Jan 11, 2008 – Jan 12, 2008 1 message not in the archive

Subsystems (4)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): 1200006638.19293.42.camel@codedot

#1 lore raw

RFC PATCH 1/2 msync: massive code cleanup of sys_msync()

The patch contains substantial code cleanup of the sys_msync() function:

1) consolidated error check for function parameters;
2) using the PAGE_ALIGN() macro instead of "manual" alignment;
3) improved readability of the loop traversing the process memory regions.

---

mm/msync.c +36 -46
index 144a757..e788f7b 100644
@@ -1,24 +1,25 @@
 /*
  *	linux/mm/msync.c
  *
+ * The msync() system call.
  * Copyright (C) 1994-1999  Linus Torvalds
+ *
+ * Substantial code cleanup.
+ * Copyright (C) 2008 Anton Salikhmetov <[email protected]>
  */
 
-/*
- * The msync() system call.
- */
+#include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/mman.h>
-#include <linux/file.h>
-#include <linux/syscalls.h>
 #include <linux/sched.h>
+#include <linux/syscalls.h>
 
 /*
  * MS_SYNC syncs the entire file - including mappings.
  *
  * MS_ASYNC does not start I/O (it used to, up to 2.5.67).
- * Nor does it marks the relevant pages dirty (it used to up to 2.6.17).
+ * Nor does it mark the relevant pages dirty (it used to up to 2.6.17).
  * Now it doesn't do anything, since dirty pages are properly tracked.
  *
  * The application may now run fsync() to
@@ -33,71 +34,60 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
 	unsigned long end;
 	struct mm_struct *mm = current->mm;
 	struct vm_area_struct *vma;
-	int unmapped_error = 0;
-	int error = -EINVAL;
+	int error = 0, unmapped_error = 0;
+
+	if ((flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC)) ||
+			(start & ~PAGE_MASK) ||
+			((flags & MS_ASYNC) && (flags & MS_SYNC)))
+		return -EINVAL;
 
-	if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC))
-		goto out;
-	if (start & ~PAGE_MASK)
-		goto out;
-	if ((flags & MS_ASYNC) && (flags & MS_SYNC))
-		goto out;
-	error = -ENOMEM;
-	len = (len + ~PAGE_MASK) & PAGE_MASK;
+	len = PAGE_ALIGN(len);
 	end = start + len;
 	if (end < start)
-		goto out;
-	error = 0;
+		return -ENOMEM;
 	if (end == start)
-		goto out;
+		return 0;
+
 	/*
 	 * If the interval [start,end) covers some unmapped address ranges,
 	 * just ignore them, but return -ENOMEM at the end.
 	 */
 	down_read(&mm->mmap_sem);
 	vma = find_vma(mm, start);
-	for (;;) {
+	do {
 		struct file *file;
 
-		/* Still start < end. */
-		error = -ENOMEM;
-		if (!vma)
-			goto out_unlock;
-		/* Here start < vma->vm_end. */
+		if (!vma) {
+			error = -ENOMEM;
+			break;
+		}
 		if (start < vma->vm_start) {
 			start = vma->vm_start;
-			if (start >= end)
-				goto out_unlock;
+			if (start >= end) {
+				error = -ENOMEM;
+				break;
+			}
 			unmapped_error = -ENOMEM;
 		}
-		/* Here vma->vm_start <= start < vma->vm_end. */
-		if ((flags & MS_INVALIDATE) &&
-				(vma->vm_flags & VM_LOCKED)) {
+		if ((flags & MS_INVALIDATE) && (vma->vm_flags & VM_LOCKED)) {
 			error = -EBUSY;
-			goto out_unlock;
+			break;
 		}
 		file = vma->vm_file;
-		start = vma->vm_end;
-		if ((flags & MS_SYNC) && file &&
-				(vma->vm_flags & VM_SHARED)) {
+		if ((flags & MS_SYNC) && file && (vma->vm_flags & VM_SHARED)) {
 			get_file(file);
 			up_read(&mm->mmap_sem);
 			error = do_fsync(file, 0);
 			fput(file);
-			if (error || start >= end)
-				goto out;
+			if (error)
+				return error;
 			down_read(&mm->mmap_sem);
-			vma = find_vma(mm, start);
-		} else {
-			if (start >= end) {
-				error = 0;
-				goto out_unlock;
-			}
-			vma = vma->vm_next;
 		}
-	}
-out_unlock:
+
+		start = vma->vm_end;
+		vma = vma->vm_next;
+	} while (start < end);
 	up_read(&mm->mmap_sem);
-out:
+
 	return error ? : unmapped_error;
 }

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#2 lore raw
Quoted 10 lines · On Fri, 11 Jan 2008 03:38:42 +0300 Anton Salikhmetov <[email protected]>…
mm/msync.c +4 -2
@@ -33,71 +34,60 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
 	unsigned long end;
 	struct mm_struct *mm = current->mm;
 	struct vm_area_struct *vma;
-	int unmapped_error = 0;
-	int error = -EINVAL;
+	int error = 0, unmapped_error = 0;
+
+	if ((flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC)) ||
+			(start & ~PAGE_MASK) ||

mm/msync.c asmlinkage long sys_msync(unsigned long start, size_t len, int flags) · line 45

Quoted 10 lines
mm/msync.c +2 -7
+			((flags & MS_ASYNC) && (flags & MS_SYNC)))
+		return -EINVAL;
 
-	if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC))
-		goto out;
-	if (start & ~PAGE_MASK)
-		goto out;
-	if ((flags & MS_ASYNC) && (flags & MS_SYNC))
-		goto out;
-	error = -ENOMEM;

Personally I prefer having these error checks separated out, but that's just my opinion :)

I like the rest of your cleanup patch.

Signature · 7 lines
All rights reversed.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#3 lore raw

RFC PATCH 2/2 msync: updating ctime and mtime at syncing

In reply to a message not in the archive

From: Anton Salikhmetov <[email protected]>

The patch contains changes for updating the ctime and mtime fields for memory mapped files:

1) adding a new flag triggering update of the inode data;
2) implementing a helper function for checking that flag and updating ctime and mtime;
3) updating time stamps for mapped files in sys_msync() and do_fsync().

---

fs/buffer.c +1 -0
index 7249e01..09adf7e 100644
@@ -719,6 +719,7 @@ static int __set_page_dirty(struct page *page,
 	}
 	write_unlock_irq(&mapping->tree_lock);
 	__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+	set_bit(AS_MCTIME, &mapping->flags);
 
 	return 1;
 }
fs/inode.c +13 -0
index ed35383..c5b954e 100644
@@ -22,6 +22,7 @@
 #include <linux/bootmem.h>
 #include <linux/inotify.h>
 #include <linux/mount.h>
+#include <linux/file.h>
 
 /*
  * This is needed for the following functions:
@@ -1282,6 +1283,18 @@ void file_update_time(struct file *file)
 
 EXPORT_SYMBOL(file_update_time);
 
+/*
+ * Update the ctime and mtime stamps after checking if they are to be updated.
+ */
+void mapped_file_update_time(struct file *file)
+{
+	if (test_and_clear_bit(AS_MCTIME, &file->f_mapping->flags)) {
+		get_file(file);
+		file_update_time(file);
+		fput(file);
+	}
+}
+
 int inode_needs_sync(struct inode *inode)
 {
 	if (IS_SYNC(inode))
fs/sync.c +2 -0
index 7cd005e..df57507 100644
@@ -87,6 +87,8 @@ long do_fsync(struct file *file, int datasync)
 		goto out;
 	}
 
+	mapped_file_update_time(file);
+
 	ret = filemap_fdatawrite(mapping);
 
 	/*
include/linux/fs.h +1 -0
index b3ec4a4..0b05118 100644
@@ -1978,6 +1978,7 @@ extern int inode_change_ok(struct inode *, struct iattr *);
 extern int __must_check inode_setattr(struct inode *, struct iattr *);
 
 extern void file_update_time(struct file *file);
+extern void mapped_file_update_time(struct file *file);
 
 static inline ino_t parent_ino(struct dentry *dentry)
 {
include/linux/pagemap.h +2 -1
index db8a410..bf0f9e7 100644
@@ -17,8 +17,9 @@
  * Bits in mapping->flags.  The lower __GFP_BITS_SHIFT bits are the page
  * allocation mode flags.
  */
-#define	AS_EIO		(__GFP_BITS_SHIFT + 0)	/* IO error on async write */
+#define AS_EIO		(__GFP_BITS_SHIFT + 0)	/* IO error on async write */
 #define AS_ENOSPC	(__GFP_BITS_SHIFT + 1)	/* ENOSPC on async write */
+#define AS_MCTIME	(__GFP_BITS_SHIFT + 2)	/* mtime and ctime to update */
 
 static inline void mapping_set_error(struct address_space *mapping, int error)
 {
mm/msync.c +16 -8
index e788f7b..9d0a8f9 100644
@@ -5,6 +5,7 @@
  * Copyright (C) 1994-1999  Linus Torvalds
  *
  * Substantial code cleanup.
+ * Updating the ctime and mtime stamps for memory mapped files.
  * Copyright (C) 2008 Anton Salikhmetov <[email protected]>
  */
 
@@ -22,6 +23,10 @@
  * Nor does it mark the relevant pages dirty (it used to up to 2.6.17).
  * Now it doesn't do anything, since dirty pages are properly tracked.
  *
+ * The msync() system call updates the ctime and mtime fields for
+ * the mapped file when called with the MS_SYNC or MS_ASYNC flags
+ * according to the POSIX standard.
+ *
  * The application may now run fsync() to
  * write out the dirty pages and wait on the writeout and check the result.
  * Or the application may run fadvise(FADV_DONTNEED) against the fd to start
@@ -74,14 +79,17 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
 			break;
 		}
 		file = vma->vm_file;
-		if ((flags & MS_SYNC) && file && (vma->vm_flags & VM_SHARED)) {
-			get_file(file);
-			up_read(&mm->mmap_sem);
-			error = do_fsync(file, 0);
-			fput(file);
-			if (error)
-				return error;
-			down_read(&mm->mmap_sem);
+		if (file && (vma->vm_flags & VM_SHARED)) {
+			mapped_file_update_time(file);
+			if (flags & MS_SYNC) {
+				get_file(file);
+				up_read(&mm->mmap_sem);
+				error = do_fsync(file, 0);
+				fput(file);
+				if (error)
+					return error;
+				down_read(&mm->mmap_sem);
+			}
 		}
 
 		start = vma->vm_end;
mm/page-writeback.c +1 -0
index d55cfca..a85df0b 100644
@@ -1025,6 +1025,7 @@ int __set_page_dirty_nobuffers(struct page *page)
 		if (mapping->host) {
 			/* !PageAnon && !swapper_space */
 			__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+			set_bit(AS_MCTIME, &mapping->flags);
 		}
 		return 1;
 	}
Acked-by on v1 2/2
#4 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

Quoted 9 lines · On Fri, 11 Jan 2008 03:38:49 +0300 Anton Salikhmetov <[email protected]>…

From: Anton Salikhmetov <[email protected]>

The patch contains changes for updating the ctime and mtime fields for memory mapped files:

1) adding a new flag triggering update of the inode data;
2) implementing a helper function for checking that flag and updating ctime and mtime;
3) updating time stamps for mapped files in sys_msync() and do_fsync().

Signature · 1 line
All rights reversed.
#5 lore raw

RFC PATCH 2/2 msync: updating ctime and mtime at syncing

In reply to a message not in the archive

The patch contains changes for updating the ctime and mtime fields for memory mapped files:

1) adding a new flag triggering update of the inode data;
2) implementing a helper function for checking that flag and updating ctime and mtime;
3) updating time stamps for mapped files in sys_msync() and do_fsync().

---

fs/buffer.c +1 -0
index 7249e01..09adf7e 100644
@@ -719,6 +719,7 @@ static int __set_page_dirty(struct page *page,
 	}
 	write_unlock_irq(&mapping->tree_lock);
 	__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+	set_bit(AS_MCTIME, &mapping->flags);
 
 	return 1;
 }
fs/inode.c +13 -0
index ed35383..c5b954e 100644
@@ -22,6 +22,7 @@
 #include <linux/bootmem.h>
 #include <linux/inotify.h>
 #include <linux/mount.h>
+#include <linux/file.h>
 
 /*
  * This is needed for the following functions:
@@ -1282,6 +1283,18 @@ void file_update_time(struct file *file)
 
 EXPORT_SYMBOL(file_update_time);
 
+/*
+ * Update the ctime and mtime stamps after checking if they are to be updated.
+ */
+void mapped_file_update_time(struct file *file)
+{
+	if (test_and_clear_bit(AS_MCTIME, &file->f_mapping->flags)) {
+		get_file(file);
+		file_update_time(file);
+		fput(file);
+	}
+}
+
 int inode_needs_sync(struct inode *inode)
 {
 	if (IS_SYNC(inode))
fs/sync.c +2 -0
index 7cd005e..df57507 100644
@@ -87,6 +87,8 @@ long do_fsync(struct file *file, int datasync)
 		goto out;
 	}
 
+	mapped_file_update_time(file);
+
 	ret = filemap_fdatawrite(mapping);
 
 	/*
include/linux/fs.h +1 -0
index b3ec4a4..0b05118 100644
@@ -1978,6 +1978,7 @@ extern int inode_change_ok(struct inode *, struct iattr *);
 extern int __must_check inode_setattr(struct inode *, struct iattr *);
 
 extern void file_update_time(struct file *file);
+extern void mapped_file_update_time(struct file *file);
 
 static inline ino_t parent_ino(struct dentry *dentry)
 {
include/linux/pagemap.h +2 -1
index db8a410..bf0f9e7 100644
@@ -17,8 +17,9 @@
  * Bits in mapping->flags.  The lower __GFP_BITS_SHIFT bits are the page
  * allocation mode flags.
  */
-#define	AS_EIO		(__GFP_BITS_SHIFT + 0)	/* IO error on async write */
+#define AS_EIO		(__GFP_BITS_SHIFT + 0)	/* IO error on async write */
 #define AS_ENOSPC	(__GFP_BITS_SHIFT + 1)	/* ENOSPC on async write */
+#define AS_MCTIME	(__GFP_BITS_SHIFT + 2)	/* mtime and ctime to update */
 
 static inline void mapping_set_error(struct address_space *mapping, int error)
 {
mm/msync.c +16 -8
index e788f7b..9d0a8f9 100644
@@ -5,6 +5,7 @@
  * Copyright (C) 1994-1999  Linus Torvalds
  *
  * Substantial code cleanup.
+ * Updating the ctime and mtime stamps for memory mapped files.
  * Copyright (C) 2008 Anton Salikhmetov <[email protected]>
  */
 
@@ -22,6 +23,10 @@
  * Nor does it mark the relevant pages dirty (it used to up to 2.6.17).
  * Now it doesn't do anything, since dirty pages are properly tracked.
  *
+ * The msync() system call updates the ctime and mtime fields for
+ * the mapped file when called with the MS_SYNC or MS_ASYNC flags
+ * according to the POSIX standard.
+ *
  * The application may now run fsync() to
  * write out the dirty pages and wait on the writeout and check the result.
  * Or the application may run fadvise(FADV_DONTNEED) against the fd to start
@@ -74,14 +79,17 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
 			break;
 		}
 		file = vma->vm_file;
-		if ((flags & MS_SYNC) && file && (vma->vm_flags & VM_SHARED)) {
-			get_file(file);
-			up_read(&mm->mmap_sem);
-			error = do_fsync(file, 0);
-			fput(file);
-			if (error)
-				return error;
-			down_read(&mm->mmap_sem);
+		if (file && (vma->vm_flags & VM_SHARED)) {
+			mapped_file_update_time(file);
+			if (flags & MS_SYNC) {
+				get_file(file);
+				up_read(&mm->mmap_sem);
+				error = do_fsync(file, 0);
+				fput(file);
+				if (error)
+					return error;
+				down_read(&mm->mmap_sem);
+			}
 		}
 
 		start = vma->vm_end;
mm/page-writeback.c +1 -0
index d55cfca..a85df0b 100644
@@ -1025,6 +1025,7 @@ int __set_page_dirty_nobuffers(struct page *page)
 		if (mapping->host) {
 			/* !PageAnon && !swapper_space */
 			__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+			set_bit(AS_MCTIME, &mapping->flags);
 		}
 		return 1;
 	}

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#6 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

Quoted 9 lines · Anton Salikhmetov

From: Anton Salikhmetov <[email protected]>

The patch contains changes for updating the ctime and mtime fields for memory mapped files:

1) adding a new flag triggering update of the inode data;
2) implementing a helper function for checking that flag and updating ctime and mtime;
3) updating time stamps for mapped files in sys_msync() and do_fsync().

What happens if the application does not issue either an msync or an fsync call, but either just munmap's the region or just keeps on manipulating it? It appears to me that the file times will never be updated in these cases.

It seems to me that the file times should be updated eventually, and perhaps even regularly if the file is being constantly updated via the mmap'd region.

Thanx...

ps

Quoted 143 lines

---

fs/buffer.c +1 -0
index 7249e01..09adf7e 100644
@@ -719,6 +719,7 @@ static int __set_page_dirty(struct page *page,
 	}
 	write_unlock_irq(&mapping->tree_lock);
 	__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+	set_bit(AS_MCTIME, &mapping->flags);
 
 	return 1;
 }
fs/inode.c +13 -0
index ed35383..c5b954e 100644
@@ -22,6 +22,7 @@
 #include <linux/bootmem.h>
 #include <linux/inotify.h>
 #include <linux/mount.h>
+#include <linux/file.h>
 
 /*
  * This is needed for the following functions:
@@ -1282,6 +1283,18 @@ void file_update_time(struct file *file)
 
 EXPORT_SYMBOL(file_update_time);
 
+/*
+ * Update the ctime and mtime stamps after checking if they are to be updated.
+ */
+void mapped_file_update_time(struct file *file)
+{
+	if (test_and_clear_bit(AS_MCTIME, &file->f_mapping->flags)) {
+		get_file(file);
+		file_update_time(file);
+		fput(file);
+	}
+}
+
 int inode_needs_sync(struct inode *inode)
 {
 	if (IS_SYNC(inode))
fs/sync.c +2 -0
index 7cd005e..df57507 100644
@@ -87,6 +87,8 @@ long do_fsync(struct file *file, int datasync)
 		goto out;
 	}
 
+	mapped_file_update_time(file);
+
 	ret = filemap_fdatawrite(mapping);
 
 	/*
include/linux/fs.h +1 -0
index b3ec4a4..0b05118 100644
@@ -1978,6 +1978,7 @@ extern int inode_change_ok(struct inode *, struct iattr *);
 extern int __must_check inode_setattr(struct inode *, struct iattr *);
 
 extern void file_update_time(struct file *file);
+extern void mapped_file_update_time(struct file *file);
 
 static inline ino_t parent_ino(struct dentry *dentry)
 {
include/linux/pagemap.h +2 -1
index db8a410..bf0f9e7 100644
@@ -17,8 +17,9 @@
  * Bits in mapping->flags.  The lower __GFP_BITS_SHIFT bits are the page
  * allocation mode flags.
  */
-#define	AS_EIO		(__GFP_BITS_SHIFT + 0)	/* IO error on async write */
+#define AS_EIO		(__GFP_BITS_SHIFT + 0)	/* IO error on async write */
 #define AS_ENOSPC	(__GFP_BITS_SHIFT + 1)	/* ENOSPC on async write */
+#define AS_MCTIME	(__GFP_BITS_SHIFT + 2)	/* mtime and ctime to update */
 
 static inline void mapping_set_error(struct address_space *mapping, int error)
 {
mm/msync.c +16 -8
index e788f7b..9d0a8f9 100644
@@ -5,6 +5,7 @@
  * Copyright (C) 1994-1999  Linus Torvalds
  *
  * Substantial code cleanup.
+ * Updating the ctime and mtime stamps for memory mapped files.
  * Copyright (C) 2008 Anton Salikhmetov <[email protected]>
  */
 
@@ -22,6 +23,10 @@
  * Nor does it mark the relevant pages dirty (it used to up to 2.6.17).
  * Now it doesn't do anything, since dirty pages are properly tracked.
  *
+ * The msync() system call updates the ctime and mtime fields for
+ * the mapped file when called with the MS_SYNC or MS_ASYNC flags
+ * according to the POSIX standard.
+ *
  * The application may now run fsync() to
  * write out the dirty pages and wait on the writeout and check the result.
  * Or the application may run fadvise(FADV_DONTNEED) against the fd to start
@@ -74,14 +79,17 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
 			break;
 		}
 		file = vma->vm_file;
-		if ((flags & MS_SYNC) && file && (vma->vm_flags & VM_SHARED)) {
-			get_file(file);
-			up_read(&mm->mmap_sem);
-			error = do_fsync(file, 0);
-			fput(file);
-			if (error)
-				return error;
-			down_read(&mm->mmap_sem);
+		if (file && (vma->vm_flags & VM_SHARED)) {
+			mapped_file_update_time(file);
+			if (flags & MS_SYNC) {
+				get_file(file);
+				up_read(&mm->mmap_sem);
+				error = do_fsync(file, 0);
+				fput(file);
+				if (error)
+					return error;
+				down_read(&mm->mmap_sem);
+			}
 		}
 
 		start = vma->vm_end;
mm/page-writeback.c +0 -0
index d55cfca..a85df0b 100644

mm/page-writeback.c int __set_page_dirty_nobuffers(struct page *page) · line 1031

Quoted 7 lines
mm/page-writeback.c +1 -0
@@ -1025,6 +1025,7 @@ int __set_page_dirty_nobuffers(struct page *page)
 		if (mapping->host) {
 			/* !PageAnon && !swapper_space */
 			__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+			set_bit(AS_MCTIME, &mapping->flags);
 		}
 		return 1;
 	}

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#7 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

2008/1/11, Peter Staubach <[email protected]>:

Quoted 11 lines
Quoted 9 lines · Anton Salikhmetov

From: Anton Salikhmetov <[email protected]>

The patch contains changes for updating the ctime and mtime fields for memory mapped files:

1) adding a new flag triggering update of the inode data;
2) implementing a helper function for checking that flag and updating ctime and mtime;
3) updating time stamps for mapped files in sys_msync() and do_fsync().

Quoted 8 lines

What happens if the application does not issue either an msync or an fsync call, but either just munmap's the region or just keeps on manipulating it? It appears to me that the file times will never be updated in these cases.

It seems to me that the file times should be updated eventually, and perhaps even regularly if the file is being constantly updated via the mmap'd region.

Indeed, FreeBSD, for example, implements updating ctime and mtime in exactly the way you described. Many people I've spoken with do interpret the POSIX requirement the same way as you do.

I had thoroughly investigated the possibility of implementing the feature you are talking about, and came to the conclusion that it would lead to quite massive changes and would require a very complicated work with locks. At least, within the current kernel design for the memory management. So, I believe that the "auto-updating" feature should be implemented later and outside of the bug #2645.

Finally, my solution puts the Linux kernel much closer to the POSIX standard (the msync() and fsync() requirements), anyway. And the changes which my patch contains, to the best of my knowledge, do not intersect with the possible implementation of
the "auto-updating" feature.

I'm planning on adding the "auto-updating" feature in the nearest future, but it looks like a separate project to me.

Quoted 140 lines
    Thanx...

       ps
Quoted 135 lines

---

fs/buffer.c +1 -0
index 7249e01..09adf7e 100644
@@ -719,6 +719,7 @@ static int __set_page_dirty(struct page *page,
      }
      write_unlock_irq(&mapping->tree_lock);
      __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+     set_bit(AS_MCTIME, &mapping->flags);
 
      return 1;
 }
fs/inode.c +13 -0
index ed35383..c5b954e 100644
@@ -22,6 +22,7 @@
 #include <linux/bootmem.h>
 #include <linux/inotify.h>
 #include <linux/mount.h>
+#include <linux/file.h>
 
 /*
  * This is needed for the following functions:
@@ -1282,6 +1283,18 @@ void file_update_time(struct file *file)
 
 EXPORT_SYMBOL(file_update_time);
 
+/*
+ * Update the ctime and mtime stamps after checking if they are to be updated.
+ */
+void mapped_file_update_time(struct file *file)
+{
+     if (test_and_clear_bit(AS_MCTIME, &file->f_mapping->flags)) {
+             get_file(file);
+             file_update_time(file);
+             fput(file);
+     }
+}
+
 int inode_needs_sync(struct inode *inode)
 {
      if (IS_SYNC(inode))
fs/sync.c +2 -0
index 7cd005e..df57507 100644
@@ -87,6 +87,8 @@ long do_fsync(struct file *file, int datasync)
              goto out;
      }
 
+     mapped_file_update_time(file);
+
      ret = filemap_fdatawrite(mapping);
 
      /*
include/linux/fs.h +1 -0
index b3ec4a4..0b05118 100644
@@ -1978,6 +1978,7 @@ extern int inode_change_ok(struct inode *, struct iattr *);
 extern int __must_check inode_setattr(struct inode *, struct iattr *);
 
 extern void file_update_time(struct file *file);
+extern void mapped_file_update_time(struct file *file);
 
 static inline ino_t parent_ino(struct dentry *dentry)
 {
include/linux/pagemap.h +2 -1
index db8a410..bf0f9e7 100644
@@ -17,8 +17,9 @@
  * Bits in mapping->flags.  The lower __GFP_BITS_SHIFT bits are the page
  * allocation mode flags.
  */
-#define      AS_EIO          (__GFP_BITS_SHIFT + 0)  /* IO error on async write */
+#define AS_EIO               (__GFP_BITS_SHIFT + 0)  /* IO error on async write */
 #define AS_ENOSPC    (__GFP_BITS_SHIFT + 1)  /* ENOSPC on async write */
+#define AS_MCTIME    (__GFP_BITS_SHIFT + 2)  /* mtime and ctime to update */
 
 static inline void mapping_set_error(struct address_space *mapping, int error)
 {
mm/msync.c +16 -8
index e788f7b..9d0a8f9 100644
@@ -5,6 +5,7 @@
  * Copyright (C) 1994-1999  Linus Torvalds
  *
  * Substantial code cleanup.
+ * Updating the ctime and mtime stamps for memory mapped files.
  * Copyright (C) 2008 Anton Salikhmetov <[email protected]>
  */
 
@@ -22,6 +23,10 @@
  * Nor does it mark the relevant pages dirty (it used to up to 2.6.17).
  * Now it doesn't do anything, since dirty pages are properly tracked.
  *
+ * The msync() system call updates the ctime and mtime fields for
+ * the mapped file when called with the MS_SYNC or MS_ASYNC flags
+ * according to the POSIX standard.
+ *
  * The application may now run fsync() to
  * write out the dirty pages and wait on the writeout and check the result.
  * Or the application may run fadvise(FADV_DONTNEED) against the fd to start
@@ -74,14 +79,17 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
                      break;
              }
              file = vma->vm_file;
-             if ((flags & MS_SYNC) && file && (vma->vm_flags & VM_SHARED)) {
-                     get_file(file);
-                     up_read(&mm->mmap_sem);
-                     error = do_fsync(file, 0);
-                     fput(file);
-                     if (error)
-                             return error;
-                     down_read(&mm->mmap_sem);
+             if (file && (vma->vm_flags & VM_SHARED)) {
+                     mapped_file_update_time(file);
+                     if (flags & MS_SYNC) {
+                             get_file(file);
+                             up_read(&mm->mmap_sem);
+                             error = do_fsync(file, 0);
+                             fput(file);
+                             if (error)
+                                     return error;
+                             down_read(&mm->mmap_sem);
+                     }
              }
 
Quoted 17 lines
Quoted 8 lines
mm/msync.c +0 -0
              start = vma->vm_end;
mm/page-writeback.c +0 -0
index d55cfca..a85df0b 100644

mm/page-writeback.c int __set_page_dirty_nobuffers(struct page *page) · line 1031

Quoted 7 lines
mm/page-writeback.c +1 -0
@@ -1025,6 +1025,7 @@ int __set_page_dirty_nobuffers(struct page *page)
              if (mapping->host) {
                      /* !PageAnon && !swapper_space */
                      __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+                     set_bit(AS_MCTIME, &mapping->flags);
              }
              return 1;
      }

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#8 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

In reply to Anton Salikhmetov
Quoted 7 lines · Anton Salikhmetov

From: Anton Salikhmetov <[email protected]>

The patch contains changes for updating the ctime and mtime fields for memory mapped files:

1) adding a new flag triggering update of the inode data;
2) implementing a helper function for checking that flag and updating ctime and mtime;
3) updating time stamps for mapped files in sys_msync() and do_fsync().

Sorry, one other issue to throw out too -- an mmap'd block device should also have its inode time fields updated. This is a little tricky because the inode referenced via mapping->host isn't the one that needs to have the time fields updated on.

I have attached the patch that I submitted last. It is quite out of date, but does show my attempt to resolve some of these issues.

Thanx...

ps

fs/buffer.c renamed from fs/buffer.c.org +7 -2
@@ -710,6 +710,7 @@ EXPORT_SYMBOL(mark_buffer_dirty_inode);
 int __set_page_dirty_buffers(struct page *page)
 {
 	struct address_space * const mapping = page_mapping(page);
+	int ret = 0;
 
 	if (unlikely(!mapping))
 		return !TestSetPageDirty(page);
@@ -727,7 +728,7 @@ int __set_page_dirty_buffers(struct page
 	spin_unlock(&mapping->private_lock);
 
 	if (TestSetPageDirty(page))
-		return 0;
+		goto out;
 
 	write_lock_irq(&mapping->tree_lock);
 	if (page->mapping) {	/* Race with truncate? */
@@ -740,7 +741,11 @@ int __set_page_dirty_buffers(struct page
 	}
 	write_unlock_irq(&mapping->tree_lock);
 	__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
-	return 1;
+	ret = 1;
+out:
+	if (page_mapped(page))
+		set_bit(AS_MCTIME, &mapping->flags);
+	return ret;
 }
 EXPORT_SYMBOL(__set_page_dirty_buffers);
 
fs/fs-writeback.c renamed from fs/fs-writeback.c.org +7 -0
@@ -167,6 +167,13 @@ __sync_single_inode(struct inode *inode,
 
 	spin_unlock(&inode_lock);
 
+	if (test_and_clear_bit(AS_MCTIME, &mapping->flags)) {
+		if (S_ISBLK(inode->i_mode))
+			bd_inode_update_time(inode);
+		else
+			inode_update_time(inode);
+	}
+
 	ret = do_writepages(mapping, wbc);
 
 	/* Don't write the inode if only I_DIRTY_PAGES was set */
fs/inode.c renamed from fs/inode.c.org +4 -5
@@ -1201,8 +1201,8 @@ void touch_atime(struct vfsmount *mnt, s
 EXPORT_SYMBOL(touch_atime);
 
 /**
- *	file_update_time	-	update mtime and ctime time
- *	@file: file accessed
+ *	inode_update_time	-	update mtime and ctime time
+ *	@inode: file accessed
  *
  *	Update the mtime and ctime members of an inode and mark the inode
  *	for writeback.  Note that this function is meant exclusively for
@@ -1212,9 +1212,8 @@ EXPORT_SYMBOL(touch_atime);
  *	timestamps are handled by the server.
  */
 
-void file_update_time(struct file *file)
+void inode_update_time(struct inode *inode)
 {
-	struct inode *inode = file->f_path.dentry->d_inode;
 	struct timespec now;
 	int sync_it = 0;
 
@@ -1238,7 +1237,7 @@ void file_update_time(struct file *file)
 		mark_inode_dirty_sync(inode);
 }
 
-EXPORT_SYMBOL(file_update_time);
+EXPORT_SYMBOL(inode_update_time);
 
 int inode_needs_sync(struct inode *inode)
 {
fs/block_dev.c renamed from fs/block_dev.c.org +16 -0
@@ -608,6 +608,22 @@ void bdput(struct block_device *bdev)
 
 EXPORT_SYMBOL(bdput);
  
+void bd_inode_update_time(struct inode *inode)
+{
+	struct block_device *bdev = inode->i_bdev;
+	struct list_head *p;
+
+	if (bdev == NULL)
+		return;
+
+	spin_lock(&bdev_lock);
+	list_for_each(p, &bdev->bd_inodes) {
+		inode = list_entry(p, struct inode, i_devices);
+		inode_update_time(inode);
+	}
+	spin_unlock(&bdev_lock);
+}
+
 static struct block_device *bd_acquire(struct inode *inode)
 {
 	struct block_device *bdev;
include/linux/fs.h renamed from include/linux/fs.h.org +6 -1
@@ -1488,6 +1488,7 @@ extern struct block_device *bdget(dev_t)
 extern void bd_set_size(struct block_device *, loff_t size);
 extern void bd_forget(struct inode *inode);
 extern void bdput(struct block_device *);
+extern void bd_inode_update_time(struct inode *);
 extern struct block_device *open_by_devnum(dev_t, unsigned);
 extern const struct address_space_operations def_blk_aops;
 #else
@@ -1892,7 +1893,11 @@ extern int buffer_migrate_page(struct ad
 extern int inode_change_ok(struct inode *, struct iattr *);
 extern int __must_check inode_setattr(struct inode *, struct iattr *);
 
-extern void file_update_time(struct file *file);
+extern void inode_update_time(struct inode *inode);
+static inline void file_update_time(struct file *file)
+{
+	inode_update_time(file->f_path.dentry->d_inode);
+}
 
 static inline ino_t parent_ino(struct dentry *dentry)
 {
include/linux/pagemap.h renamed from include/linux/pagemap.h.org +2 -1
@@ -16,8 +16,9 @@
  * Bits in mapping->flags.  The lower __GFP_BITS_SHIFT bits are the page
  * allocation mode flags.
  */
-#define	AS_EIO		(__GFP_BITS_SHIFT + 0)	/* IO error on async write */
+#define AS_EIO		(__GFP_BITS_SHIFT + 0)	/* IO error on async write */
 #define AS_ENOSPC	(__GFP_BITS_SHIFT + 1)	/* ENOSPC on async write */
+#define AS_MCTIME	(__GFP_BITS_SHIFT + 2)	/* need m/ctime change */
 
 static inline gfp_t mapping_gfp_mask(struct address_space * mapping)
 {
mm/page-writeback.c renamed from mm/page-writeback.c.org +7 -3
@@ -760,8 +760,10 @@ int __set_page_dirty_no_writeback(struct
  */
 int __set_page_dirty_nobuffers(struct page *page)
 {
+	struct address_space *mapping = page_mapping(page);
+	int ret = 0;
+
 	if (!TestSetPageDirty(page)) {
-		struct address_space *mapping = page_mapping(page);
 		struct address_space *mapping2;
 
 		if (!mapping)
@@ -783,9 +785,11 @@ int __set_page_dirty_nobuffers(struct pa
 			/* !PageAnon && !swapper_space */
 			__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
 		}
-		return 1;
+		ret = 1;
 	}
-	return 0;
+	if (page_mapped(page))
+		set_bit(AS_MCTIME, &mapping->flags);
+	return ret;
 }
 EXPORT_SYMBOL(__set_page_dirty_nobuffers);
 
mm/msync.c renamed from mm/msync.c.org +9 -3
@@ -12,6 +12,7 @@
 #include <linux/mman.h>
 #include <linux/file.h>
 #include <linux/syscalls.h>
+#include <linux/pagemap.h>
 
 /*
  * MS_SYNC syncs the entire file - including mappings.
@@ -77,11 +78,16 @@ asmlinkage long sys_msync(unsigned long 
 		}
 		file = vma->vm_file;
 		start = vma->vm_end;
-		if ((flags & MS_SYNC) && file &&
-				(vma->vm_flags & VM_SHARED)) {
+		if ((flags & (MS_SYNC|MS_ASYNC)) &&
+		    file && (vma->vm_flags & VM_SHARED)) {
 			get_file(file);
 			up_read(&mm->mmap_sem);
-			error = do_fsync(file, 0);
+			error = 0;
+			if (flags & MS_SYNC)
+				error = do_fsync(file, 0);
+			else if (test_and_clear_bit(AS_MCTIME,
+					       &file->f_mapping->flags))
+				file_update_time(file);
 			fput(file);
 			if (error || start >= end)
 				goto out;
#9 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

2008/1/11, Peter Staubach <[email protected]>:

Quoted 9 lines
Quoted 7 lines · Anton Salikhmetov

From: Anton Salikhmetov <[email protected]>

The patch contains changes for updating the ctime and mtime fields for memory mapped files:

1) adding a new flag triggering update of the inode data;
2) implementing a helper function for checking that flag and updating ctime and mtime;
3) updating time stamps for mapped files in sys_msync() and do_fsync().

Quoted 7 lines

Sorry, one other issue to throw out too -- an mmap'd block device should also have its inode time fields updated. This is a little tricky because the inode referenced via mapping->host isn't the one that needs to have the time fields updated on.

I have attached the patch that I submitted last. It is quite out of date, but does show my attempt to resolve some of these issues.

Thanks for your feedback!

Now I'm looking at your solution and thinking about which parts of it I could adapt to the infrastructure I'm trying to develop.

However, I would like to address the block device case within
a separate project. But for now, I want the msync() and fsync() system calls to update ctime and mtime at least for memory-mapped regular files properly. I feel that even this little improvement could address many customer's troubles such as the one Jacob Oestergaard reported in the bug #2645.

Quoted 197 lines
    Thanx...

       ps
fs/buffer.c renamed from fs/buffer.c.org +7 -2
@@ -710,6 +710,7 @@ EXPORT_SYMBOL(mark_buffer_dirty_inode);
 int __set_page_dirty_buffers(struct page *page)
 {
        struct address_space * const mapping = page_mapping(page);
+       int ret = 0;
 
        if (unlikely(!mapping))
                return !TestSetPageDirty(page);
@@ -727,7 +728,7 @@ int __set_page_dirty_buffers(struct page
        spin_unlock(&mapping->private_lock);
 
        if (TestSetPageDirty(page))
-               return 0;
+               goto out;
 
        write_lock_irq(&mapping->tree_lock);
        if (page->mapping) {    /* Race with truncate? */
@@ -740,7 +741,11 @@ int __set_page_dirty_buffers(struct page
        }
        write_unlock_irq(&mapping->tree_lock);
        __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
-       return 1;
+       ret = 1;
+out:
+       if (page_mapped(page))
+               set_bit(AS_MCTIME, &mapping->flags);
+       return ret;
 }
 EXPORT_SYMBOL(__set_page_dirty_buffers);
 
fs/fs-writeback.c renamed from fs/fs-writeback.c.org +7 -0
@@ -167,6 +167,13 @@ __sync_single_inode(struct inode *inode,
 
        spin_unlock(&inode_lock);
 
+       if (test_and_clear_bit(AS_MCTIME, &mapping->flags)) {
+               if (S_ISBLK(inode->i_mode))
+                       bd_inode_update_time(inode);
+               else
+                       inode_update_time(inode);
+       }
+
        ret = do_writepages(mapping, wbc);
 
        /* Don't write the inode if only I_DIRTY_PAGES was set */
fs/inode.c renamed from fs/inode.c.org +4 -5
@@ -1201,8 +1201,8 @@ void touch_atime(struct vfsmount *mnt, s
 EXPORT_SYMBOL(touch_atime);
 
 /**
- *     file_update_time        -       update mtime and ctime time
- *     @file: file accessed
+ *     inode_update_time       -       update mtime and ctime time
+ *     @inode: file accessed
  *
  *     Update the mtime and ctime members of an inode and mark the inode
  *     for writeback.  Note that this function is meant exclusively for
@@ -1212,9 +1212,8 @@ EXPORT_SYMBOL(touch_atime);
  *     timestamps are handled by the server.
  */
 
-void file_update_time(struct file *file)
+void inode_update_time(struct inode *inode)
 {
-       struct inode *inode = file->f_path.dentry->d_inode;
        struct timespec now;
        int sync_it = 0;
 
@@ -1238,7 +1237,7 @@ void file_update_time(struct file *file)
                mark_inode_dirty_sync(inode);
 }
 
-EXPORT_SYMBOL(file_update_time);
+EXPORT_SYMBOL(inode_update_time);
 
 int inode_needs_sync(struct inode *inode)
 {
fs/block_dev.c renamed from fs/block_dev.c.org +16 -0
@@ -608,6 +608,22 @@ void bdput(struct block_device *bdev)
 
 EXPORT_SYMBOL(bdput);
 
+void bd_inode_update_time(struct inode *inode)
+{
+       struct block_device *bdev = inode->i_bdev;
+       struct list_head *p;
+
+       if (bdev == NULL)
+               return;
+
+       spin_lock(&bdev_lock);
+       list_for_each(p, &bdev->bd_inodes) {
+               inode = list_entry(p, struct inode, i_devices);
+               inode_update_time(inode);
+       }
+       spin_unlock(&bdev_lock);
+}
+
 static struct block_device *bd_acquire(struct inode *inode)
 {
        struct block_device *bdev;
include/linux/fs.h renamed from include/linux/fs.h.org +6 -1
@@ -1488,6 +1488,7 @@ extern struct block_device *bdget(dev_t)
 extern void bd_set_size(struct block_device *, loff_t size);
 extern void bd_forget(struct inode *inode);
 extern void bdput(struct block_device *);
+extern void bd_inode_update_time(struct inode *);
 extern struct block_device *open_by_devnum(dev_t, unsigned);
 extern const struct address_space_operations def_blk_aops;
 #else
@@ -1892,7 +1893,11 @@ extern int buffer_migrate_page(struct ad
 extern int inode_change_ok(struct inode *, struct iattr *);
 extern int __must_check inode_setattr(struct inode *, struct iattr *);
 
-extern void file_update_time(struct file *file);
+extern void inode_update_time(struct inode *inode);
+static inline void file_update_time(struct file *file)
+{
+       inode_update_time(file->f_path.dentry->d_inode);
+}
 
 static inline ino_t parent_ino(struct dentry *dentry)
 {
include/linux/pagemap.h renamed from include/linux/pagemap.h.org +2 -1
@@ -16,8 +16,9 @@
  * Bits in mapping->flags.  The lower __GFP_BITS_SHIFT bits are the page
  * allocation mode flags.
  */
-#define        AS_EIO          (__GFP_BITS_SHIFT + 0)  /* IO error on async write */
+#define AS_EIO         (__GFP_BITS_SHIFT + 0)  /* IO error on async write */
 #define AS_ENOSPC      (__GFP_BITS_SHIFT + 1)  /* ENOSPC on async write */
+#define AS_MCTIME      (__GFP_BITS_SHIFT + 2)  /* need m/ctime change */
 
 static inline gfp_t mapping_gfp_mask(struct address_space * mapping)
 {
mm/page-writeback.c renamed from mm/page-writeback.c.org +7 -3
@@ -760,8 +760,10 @@ int __set_page_dirty_no_writeback(struct
  */
 int __set_page_dirty_nobuffers(struct page *page)
 {
+       struct address_space *mapping = page_mapping(page);
+       int ret = 0;
+
        if (!TestSetPageDirty(page)) {
-               struct address_space *mapping = page_mapping(page);
                struct address_space *mapping2;
 
                if (!mapping)
@@ -783,9 +785,11 @@ int __set_page_dirty_nobuffers(struct pa
                        /* !PageAnon && !swapper_space */
                        __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
                }
-               return 1;
+               ret = 1;
        }
-       return 0;
+       if (page_mapped(page))
+               set_bit(AS_MCTIME, &mapping->flags);
+       return ret;
 }
 EXPORT_SYMBOL(__set_page_dirty_nobuffers);
 
mm/msync.c renamed from mm/msync.c.org +3 -2
@@ -12,6 +12,7 @@
 #include <linux/mman.h>
 #include <linux/file.h>
 #include <linux/syscalls.h>
+#include <linux/pagemap.h>
 
 /*
  * MS_SYNC syncs the entire file - including mappings.
@@ -77,11 +78,16 @@ asmlinkage long sys_msync(unsigned long
                }
                file = vma->vm_file;
                start = vma->vm_end;
-               if ((flags & MS_SYNC) && file &&
-                               (vma->vm_flags & VM_SHARED)) {
+               if ((flags & (MS_SYNC|MS_ASYNC)) &&
+                   file && (vma->vm_flags & VM_SHARED)) {
                        get_file(file);
                        up_read(&mm->mmap_sem);

mm/msync.c asmlinkage long sys_msync(unsigned long · line 93

Quoted 10 lines
mm/msync.c renamed from mm/msync.c.org +6 -1
-                       error = do_fsync(file, 0);
+                       error = 0;
+                       if (flags & MS_SYNC)
+                               error = do_fsync(file, 0);
+                       else if (test_and_clear_bit(AS_MCTIME,
+                                              &file->f_mapping->flags))
+                               file_update_time(file);
                        fput(file);
                        if (error || start >= end)
                                goto out;

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#10 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

Quoted 26 lines · Anton Salikhmetov

2008/1/11, Peter Staubach <[email protected]>:

Quoted 10 lines
Quoted 8 lines · Anton Salikhmetov

From: Anton Salikhmetov <[email protected]>

The patch contains changes for updating the ctime and mtime fields for memory mapped files:

1) adding a new flag triggering update of the inode data;
2) implementing a helper function for checking that flag and updating ctime and mtime;
3) updating time stamps for mapped files in sys_msync() and do_fsync().

Quoted 8 lines

Sorry, one other issue to throw out too -- an mmap'd block device should also have its inode time fields updated. This is a little tricky because the inode referenced via mapping->host isn't the one that needs to have the time fields updated on.

I have attached the patch that I submitted last. It is quite out of date, but does show my attempt to resolve some of these issues.

Thanks for your feedback!

Now I'm looking at your solution and thinking about which parts of it I could adapt to the infrastructure I'm trying to develop.

Quoted 6 lines

However, I would like to address the block device case within
a separate project. But for now, I want the msync() and fsync() system calls to update ctime and mtime at least for memory-mapped regular files properly. I feel that even this little improvement could address many customer's troubles such as the one Jacob Oestergaard reported in the bug #2645.

Not that I disagree and I also have customers who would really like to see this situation addressed so that I can then fix it in RHEL, but the block device issue was raised by Andrew Morton during my first attempt to get a patch integrated.

Just so that you are aware of who has raised which issues... :-)

Thanx...

ps

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#11 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

2008/1/12, Peter Staubach <[email protected]>:

Quoted 34 lines
Quoted 26 lines · Anton Salikhmetov

2008/1/11, Peter Staubach <[email protected]>:

Quoted 10 lines
Quoted 8 lines · Anton Salikhmetov

From: Anton Salikhmetov <[email protected]>

The patch contains changes for updating the ctime and mtime fields for memory mapped files:

1) adding a new flag triggering update of the inode data;
2) implementing a helper function for checking that flag and updating ctime and mtime;
3) updating time stamps for mapped files in sys_msync() and do_fsync().

Quoted 8 lines

Sorry, one other issue to throw out too -- an mmap'd block device should also have its inode time fields updated. This is a little tricky because the inode referenced via mapping->host isn't the one that needs to have the time fields updated on.

I have attached the patch that I submitted last. It is quite out of date, but does show my attempt to resolve some of these issues.

Thanks for your feedback!

Now I'm looking at your solution and thinking about which parts of it I could adapt to the infrastructure I'm trying to develop.

Quoted 6 lines

However, I would like to address the block device case within
a separate project. But for now, I want the msync() and fsync() system calls to update ctime and mtime at least for memory-mapped regular files properly. I feel that even this little improvement could address many customer's troubles such as the one Jacob Oestergaard reported in the bug #2645.

Quoted 6 lines

Not that I disagree and I also have customers who would really like to see this situation addressed so that I can then fix it in RHEL, but the block device issue was raised by Andrew Morton during my first attempt to get a patch integrated.

Just so that you are aware of who has raised which issues... :-)

Yes, I remember that email by Andrew Morton (http://lkml.org/lkml/2006/6/19/6).
In fact, I went over that thread many times while working on my solution for this bug.

Nevertheless, I presume the block device case to be addressed in a separate patch
series, just like the "auto-updating" feature.

Quoted 5 lines
    Thanx...

       ps

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#12 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

In reply to Peter Staubach

2008/1/11, Peter Staubach <[email protected]>:

Quoted 9 lines
Quoted 7 lines · Anton Salikhmetov

From: Anton Salikhmetov <[email protected]>

The patch contains changes for updating the ctime and mtime fields for memory mapped files:

1) adding a new flag triggering update of the inode data;
2) implementing a helper function for checking that flag and updating ctime and mtime;
3) updating time stamps for mapped files in sys_msync() and do_fsync().

Quoted 7 lines

Sorry, one other issue to throw out too -- an mmap'd block device should also have its inode time fields updated. This is a little tricky because the inode referenced via mapping->host isn't the one that needs to have the time fields updated on.

I have attached the patch that I submitted last. It is quite out of date, but does show my attempt to resolve some of these issues.

It looks very strange to me that your patch received no reaction whatsoever:

http://lkml.org/lkml/2007/2/20/255

I've just tried to adapt your patch to my solution. I'm afraid ctime and mtime stamps
for memory-mapped block device files are still not updated with your code integrated
into what I'm doing. I set up the loopback device /dev/loop0, then ran my test program:

http://bugzilla.kernel.org/attachment.cgi?id=14398

The unit test shows that ctime and mtime are not updated. However, regular files are updated properly.

Also I have a couple of questions about your patch. Please see below.

Quoted 40 lines
    Thanx...

       ps
fs/buffer.c renamed from fs/buffer.c.org +7 -2
@@ -710,6 +710,7 @@ EXPORT_SYMBOL(mark_buffer_dirty_inode);
 int __set_page_dirty_buffers(struct page *page)
 {
        struct address_space * const mapping = page_mapping(page);
+       int ret = 0;
 
        if (unlikely(!mapping))
                return !TestSetPageDirty(page);
@@ -727,7 +728,7 @@ int __set_page_dirty_buffers(struct page
        spin_unlock(&mapping->private_lock);
 
        if (TestSetPageDirty(page))
-               return 0;
+               goto out;
 
        write_lock_irq(&mapping->tree_lock);
        if (page->mapping) {    /* Race with truncate? */
@@ -740,7 +741,11 @@ int __set_page_dirty_buffers(struct page
        }
        write_unlock_irq(&mapping->tree_lock);
        __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
-       return 1;
+       ret = 1;
+out:
+       if (page_mapped(page))
+               set_bit(AS_MCTIME, &mapping->flags);
+       return ret;
 }
 EXPORT_SYMBOL(__set_page_dirty_buffers);
 

fs/fs-writeback.c __sync_single_inode(struct inode *inode, · line 176

Quoted 10 lines
fs/fs-writeback.c renamed from fs/fs-writeback.c.org +7 -0
@@ -167,6 +167,13 @@ __sync_single_inode(struct inode *inode,
 
        spin_unlock(&inode_lock);
 
+       if (test_and_clear_bit(AS_MCTIME, &mapping->flags)) {
+               if (S_ISBLK(inode->i_mode))
+                       bd_inode_update_time(inode);
+               else
+                       inode_update_time(inode);
+       }
+

Why the S_ISBLK check is done only here? I think sys_msync() should contain the same logic.

Quoted 142 lines
fs/fs-writeback.c renamed from fs/fs-writeback.c.org +0 -0
        ret = do_writepages(mapping, wbc);
 
        /* Don't write the inode if only I_DIRTY_PAGES was set */
fs/inode.c renamed from fs/inode.c.org +4 -5
@@ -1201,8 +1201,8 @@ void touch_atime(struct vfsmount *mnt, s
 EXPORT_SYMBOL(touch_atime);
 
 /**
- *     file_update_time        -       update mtime and ctime time
- *     @file: file accessed
+ *     inode_update_time       -       update mtime and ctime time
+ *     @inode: file accessed
  *
  *     Update the mtime and ctime members of an inode and mark the inode
  *     for writeback.  Note that this function is meant exclusively for
@@ -1212,9 +1212,8 @@ EXPORT_SYMBOL(touch_atime);
  *     timestamps are handled by the server.
  */
 
-void file_update_time(struct file *file)
+void inode_update_time(struct inode *inode)
 {
-       struct inode *inode = file->f_path.dentry->d_inode;
        struct timespec now;
        int sync_it = 0;
 
@@ -1238,7 +1237,7 @@ void file_update_time(struct file *file)
                mark_inode_dirty_sync(inode);
 }
 
-EXPORT_SYMBOL(file_update_time);
+EXPORT_SYMBOL(inode_update_time);
 
 int inode_needs_sync(struct inode *inode)
 {
fs/block_dev.c renamed from fs/block_dev.c.org +16 -0
@@ -608,6 +608,22 @@ void bdput(struct block_device *bdev)
 
 EXPORT_SYMBOL(bdput);
 
+void bd_inode_update_time(struct inode *inode)
+{
+       struct block_device *bdev = inode->i_bdev;
+       struct list_head *p;
+
+       if (bdev == NULL)
+               return;
+
+       spin_lock(&bdev_lock);
+       list_for_each(p, &bdev->bd_inodes) {
+               inode = list_entry(p, struct inode, i_devices);
+               inode_update_time(inode);
+       }
+       spin_unlock(&bdev_lock);
+}
+
 static struct block_device *bd_acquire(struct inode *inode)
 {
        struct block_device *bdev;
include/linux/fs.h renamed from include/linux/fs.h.org +6 -1
@@ -1488,6 +1488,7 @@ extern struct block_device *bdget(dev_t)
 extern void bd_set_size(struct block_device *, loff_t size);
 extern void bd_forget(struct inode *inode);
 extern void bdput(struct block_device *);
+extern void bd_inode_update_time(struct inode *);
 extern struct block_device *open_by_devnum(dev_t, unsigned);
 extern const struct address_space_operations def_blk_aops;
 #else
@@ -1892,7 +1893,11 @@ extern int buffer_migrate_page(struct ad
 extern int inode_change_ok(struct inode *, struct iattr *);
 extern int __must_check inode_setattr(struct inode *, struct iattr *);
 
-extern void file_update_time(struct file *file);
+extern void inode_update_time(struct inode *inode);
+static inline void file_update_time(struct file *file)
+{
+       inode_update_time(file->f_path.dentry->d_inode);
+}
 
 static inline ino_t parent_ino(struct dentry *dentry)
 {
include/linux/pagemap.h renamed from include/linux/pagemap.h.org +2 -1
@@ -16,8 +16,9 @@
  * Bits in mapping->flags.  The lower __GFP_BITS_SHIFT bits are the page
  * allocation mode flags.
  */
-#define        AS_EIO          (__GFP_BITS_SHIFT + 0)  /* IO error on async write */
+#define AS_EIO         (__GFP_BITS_SHIFT + 0)  /* IO error on async write */
 #define AS_ENOSPC      (__GFP_BITS_SHIFT + 1)  /* ENOSPC on async write */
+#define AS_MCTIME      (__GFP_BITS_SHIFT + 2)  /* need m/ctime change */
 
 static inline gfp_t mapping_gfp_mask(struct address_space * mapping)
 {
mm/page-writeback.c renamed from mm/page-writeback.c.org +7 -3
@@ -760,8 +760,10 @@ int __set_page_dirty_no_writeback(struct
  */
 int __set_page_dirty_nobuffers(struct page *page)
 {
+       struct address_space *mapping = page_mapping(page);
+       int ret = 0;
+
        if (!TestSetPageDirty(page)) {
-               struct address_space *mapping = page_mapping(page);
                struct address_space *mapping2;
 
                if (!mapping)
@@ -783,9 +785,11 @@ int __set_page_dirty_nobuffers(struct pa
                        /* !PageAnon && !swapper_space */
                        __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
                }
-               return 1;
+               ret = 1;
        }
-       return 0;
+       if (page_mapped(page))
+               set_bit(AS_MCTIME, &mapping->flags);
+       return ret;
 }
 EXPORT_SYMBOL(__set_page_dirty_nobuffers);
 
mm/msync.c renamed from mm/msync.c.org +2 -2
@@ -12,6 +12,7 @@
 #include <linux/mman.h>
 #include <linux/file.h>
 #include <linux/syscalls.h>
+#include <linux/pagemap.h>
 
 /*
  * MS_SYNC syncs the entire file - including mappings.
@@ -77,11 +78,16 @@ asmlinkage long sys_msync(unsigned long
                }
                file = vma->vm_file;
                start = vma->vm_end;
-               if ((flags & MS_SYNC) && file &&
-                               (vma->vm_flags & VM_SHARED)) {
+               if ((flags & (MS_SYNC|MS_ASYNC)) &&

mm/msync.c asmlinkage long sys_msync(unsigned long · line 90

Quoted 10 lines
mm/msync.c renamed from mm/msync.c.org +7 -1
+                   file && (vma->vm_flags & VM_SHARED)) {
                        get_file(file);
                        up_read(&mm->mmap_sem);
-                       error = do_fsync(file, 0);
+                       error = 0;
+                       if (flags & MS_SYNC)
+                               error = do_fsync(file, 0);
+                       else if (test_and_clear_bit(AS_MCTIME,
+                                              &file->f_mapping->flags))
+                               file_update_time(file);

The msync() function might be called for a block device as well.

mm/msync.c asmlinkage long sys_msync(unsigned long · line 93

Quoted 5 lines
mm/msync.c renamed from mm/msync.c.org +0 -0
                        fput(file);
                        if (error || start >= end)
                                goto out;

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#13 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

In reply to Anton Salikhmetov

fs/inode.c void file_update_time(struct file *file) · line 1297

Quoted 12 lines · Anton Salikhmetov
fs/inode.c +12 -0
+/*
+ * Update the ctime and mtime stamps after checking if they are to be updated.
+ */
+void mapped_file_update_time(struct file *file)
+{
+	if (test_and_clear_bit(AS_MCTIME, &file->f_mapping->flags)) {
+		get_file(file);
+		file_update_time(file);
+		fput(file);
+	}
+}
+

I don't think you need the get/put file stuff here, because

fs/sync.c long do_fsync(struct file *file, int datasync) · line 94

Quoted 9 lines
fs/sync.c +2 -0
@@ -87,6 +87,8 @@ long do_fsync(struct file *file, int datasync)
 		goto out;
 	}
 
+	mapped_file_update_time(file);
+
 	ret = filemap_fdatawrite(mapping);
 
 	/*

at this call-site we already hold an extra reference on the file, and

Quoted 16 lines
mm/msync.c +4 -8
@@ -74,14 +79,17 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
 			break;
 		}
 		file = vma->vm_file;
-		if ((flags & MS_SYNC) && file && (vma->vm_flags & VM_SHARED)) {
-			get_file(file);
-			up_read(&mm->mmap_sem);
-			error = do_fsync(file, 0);
-			fput(file);
-			if (error)
-				return error;
-			down_read(&mm->mmap_sem);
+		if (file && (vma->vm_flags & VM_SHARED)) {
+			mapped_file_update_time(file);
+			if (flags & MS_SYNC) {
+				get_file(file);

mm/msync.c asmlinkage long sys_msync(unsigned long start, size_t len, int flags) · line 95

Quoted 10 lines
mm/msync.c +7 -0
+				up_read(&mm->mmap_sem);
+				error = do_fsync(file, 0);
+				fput(file);
+				if (error)
+					return error;
+				down_read(&mm->mmap_sem);
+			}
 		}
 
 		start = vma->vm_end;

here we hold the mmap_sem so the vma reference on the file can't go away.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#14 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

Quoted 16 lines · Peter Zijlstra

fs/inode.c void file_update_time(struct file *file) · line 1297

Quoted 12 lines · Anton Salikhmetov
fs/inode.c +12 -0
+/*
+ * Update the ctime and mtime stamps after checking if they are to be updated.
+ */
+void mapped_file_update_time(struct file *file)
+{
+	if (test_and_clear_bit(AS_MCTIME, &file->f_mapping->flags)) {
+		get_file(file);
+		file_update_time(file);
+		fput(file);
+	}
+}
+

I don't think you need the get/put file stuff here, because

BTW, the reason for me noticing this is that if it would be needed there is a race condition right there, who is to say that the file pointer you're deref'ing in your test condition isn't a dead one already.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#15 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

2008/1/12, Peter Zijlstra <[email protected]>:

Quoted 19 lines · Peter Zijlstra
Quoted 16 lines · Anton Salikhmetov

fs/inode.c void file_update_time(struct file *file) · line 1297

Quoted 13 lines
fs/inode.c +12 -0
+/*
+ * Update the ctime and mtime stamps after checking if they are to be updated.
+ */
+void mapped_file_update_time(struct file *file)
+{
+   if (test_and_clear_bit(AS_MCTIME, &file->f_mapping->flags)) {
+           get_file(file);
+           file_update_time(file);
+           fput(file);
+   }
+}
+

I don't think you need the get/put file stuff here, because

Quoted 3 lines

BTW, the reason for me noticing this is that if it would be needed there is a race condition right there, who is to say that the file pointer you're deref'ing in your test condition isn't a dead one already.

So, in your opinion, is it at all needed here to play with the file reference counter? May I drop the get_file() and fput() calls from the sys_msync() function?

Quoted 2 lines

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#16 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

Quoted 20 lines · Anton Salikhmetov

2008/1/12, Peter Zijlstra <[email protected]>:

Quoted 19 lines · Peter Zijlstra
Quoted 16 lines · Anton Salikhmetov

fs/inode.c void file_update_time(struct file *file) · line 1297

Quoted 13 lines
fs/inode.c +12 -0
+/*
+ * Update the ctime and mtime stamps after checking if they are to be updated.
+ */
+void mapped_file_update_time(struct file *file)
+{
+   if (test_and_clear_bit(AS_MCTIME, &file->f_mapping->flags)) {
+           get_file(file);
+           file_update_time(file);
+           fput(file);
+   }
+}
+

I don't think you need the get/put file stuff here, because

Quoted 7 lines
Quoted 3 lines

BTW, the reason for me noticing this is that if it would be needed there is a race condition right there, who is to say that the file pointer you're deref'ing in your test condition isn't a dead one already.

So, in your opinion, is it at all needed here to play with the file reference counter? May I drop the get_file() and fput() calls from the sys_msync() function?

No, the ones in sys_msync() around calling do_fsync() are most definately needed because we release mmap_sem there.

What I'm saying is that you can remove the get_file()/fput() calls from your new mapped_file_update_time() function.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#17 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

2008/1/12, Peter Zijlstra <[email protected]>:

Quoted 30 lines · Anton Salikhmetov
Quoted 20 lines

2008/1/12, Peter Zijlstra <[email protected]>:

Quoted 19 lines · Peter Zijlstra
Quoted 16 lines · Anton Salikhmetov

fs/inode.c void file_update_time(struct file *file) · line 1297

Quoted 13 lines
fs/inode.c +12 -0
+/*
+ * Update the ctime and mtime stamps after checking if they are to be updated.
+ */
+void mapped_file_update_time(struct file *file)
+{
+   if (test_and_clear_bit(AS_MCTIME, &file->f_mapping->flags)) {
+           get_file(file);
+           file_update_time(file);
+           fput(file);
+   }
+}
+

I don't think you need the get/put file stuff here, because

Quoted 7 lines
Quoted 3 lines

BTW, the reason for me noticing this is that if it would be needed there is a race condition right there, who is to say that the file pointer you're deref'ing in your test condition isn't a dead one already.

So, in your opinion, is it at all needed here to play with the file reference counter? May I drop the get_file() and fput() calls from the sys_msync() function?

Quoted 5 lines

No, the ones in sys_msync() around calling do_fsync() are most definately needed because we release mmap_sem there.

What I'm saying is that you can remove the get_file()/fput() calls from your new mapped_file_update_time() function.

OK, thank you very much for your answer. I'm planning to submit my next solution which is going to take your suggestion into account.

But I'm not sure how to process memory-mapped block device files. Staubach's approach did not work for me after adapting it to my solution.

Quoted 2 lines

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#18 lore raw

Re: [PATCH 2/2][RFC][BUG] msync: updating ctime and mtime at syncing

In reply to Peter Zijlstra

2008/1/12, Peter Zijlstra <[email protected]>:

Quoted 55 lines · Anton Salikhmetov

fs/inode.c void file_update_time(struct file *file) · line 1297

Quoted 13 lines
fs/inode.c +12 -0
+/*
+ * Update the ctime and mtime stamps after checking if they are to be updated.
+ */
+void mapped_file_update_time(struct file *file)
+{
+     if (test_and_clear_bit(AS_MCTIME, &file->f_mapping->flags)) {
+             get_file(file);
+             file_update_time(file);
+             fput(file);
+     }
+}
+

I don't think you need the get/put file stuff here, because

fs/sync.c long do_fsync(struct file *file, int datasync) · line 94

Quoted 9 lines
fs/sync.c +2 -0
@@ -87,6 +87,8 @@ long do_fsync(struct file *file, int datasync)
              goto out;
      }
 
+     mapped_file_update_time(file);
+
      ret = filemap_fdatawrite(mapping);
 
      /*

at this call-site we already hold an extra reference on the file, and

Quoted 25 lines
mm/msync.c +11 -8
@@ -74,14 +79,17 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
                      break;
              }
              file = vma->vm_file;
-             if ((flags & MS_SYNC) && file && (vma->vm_flags & VM_SHARED)) {
-                     get_file(file);
-                     up_read(&mm->mmap_sem);
-                     error = do_fsync(file, 0);
-                     fput(file);
-                     if (error)
-                             return error;
-                     down_read(&mm->mmap_sem);
+             if (file && (vma->vm_flags & VM_SHARED)) {
+                     mapped_file_update_time(file);
+                     if (flags & MS_SYNC) {
+                             get_file(file);
+                             up_read(&mm->mmap_sem);
+                             error = do_fsync(file, 0);
+                             fput(file);
+                             if (error)
+                                     return error;
+                             down_read(&mm->mmap_sem);
+                     }
              }
 
Quoted 4 lines

mm/msync.c asmlinkage long sys_msync(unsigned long start, size_t len, int flags) · line 95

Quoted 1 line
mm/msync.c +0 -0
              start = vma->vm_end;

here we hold the mmap_sem so the vma reference on the file can't go away.

These get_file() and fput() calls were in the sys_msync() function form before all my changes. I did not add them here.

Quoted 2 lines

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>