[RFC PATCH] ovl: copy-up on MAP_SHARED

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When a read-only file is mapped shared, copy up the file before
actually doing the map so that we can keep data consistency in
O_RDONLY/O_WRONLY combination of shared mapping.

Signed-off-by: Chengguang Xu <cgxu519@xxxxxxxxxxxx>
---
 fs/overlayfs/Kconfig     | 21 ++++++++++++++++
 fs/overlayfs/file.c      | 54 ++++++++++++++++++++++++++++++++++++++++
 fs/overlayfs/overlayfs.h |  6 +++++
 fs/overlayfs/ovl_entry.h |  1 +
 fs/overlayfs/super.c     | 22 ++++++++++++++++
 5 files changed, 104 insertions(+)

diff --git a/fs/overlayfs/Kconfig b/fs/overlayfs/Kconfig
index 444e2da4f60e..e9ce3010d5c7 100644
--- a/fs/overlayfs/Kconfig
+++ b/fs/overlayfs/Kconfig
@@ -123,3 +123,24 @@ config OVERLAY_FS_METACOPY
 	  that doesn't support this feature will have unexpected results.
 
 	  If unsure, say N.
+
+config OVERLAY_FS_COPY_UP_SHARED
+	bool "Overlayfs: copy up when mapping a file shared"
+	default n
+	depends on OVERLAY_FS
+	help
+	  If this option is enabled then on mapping a file with MAP_SHARED
+	  overlayfs copies up the file in anticipation of it being modified (just
+	  like we copy up the file on O_WRONLY and O_RDWR in anticipation of
+	  modification).  This does not interfere with shared library loading, as
+	  that uses MAP_PRIVATE.  But there might be use cases out there where
+	  this impacts performance and disk usage.
+
+	  This just selects the default, the feature can also be enabled or
+	  disabled in the running kernel or individually on each overlay mount.
+
+	  To get maximally standard compliant behavior, enable this option.
+
+	  To get a maximally backward compatible kernel, disable this option.
+
+	  If unsure, say N.
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index a5317216de73..69d4636d79ad 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -12,6 +12,7 @@
 #include <linux/splice.h>
 #include <linux/mm.h>
 #include <linux/fs.h>
+#include <linux/mman.h>
 #include "overlayfs.h"
 
 struct ovl_aio_req {
@@ -429,12 +430,65 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 	return ret;
 }
 
+struct ovl_copy_up_work {
+	struct work_struct work;
+	struct dentry *dentry;
+	unsigned long flags;
+	int err;
+};
+
+enum ovl_copy_up_work_flag {
+	OVL_COPY_UP_PENDING,
+};
+
+static void ovl_copy_up_work_fn(struct work_struct *work)
+{
+	struct ovl_copy_up_work *ovl_cuw;
+
+	ovl_cuw = container_of(work, struct ovl_copy_up_work, work);
+	ovl_cuw->err = ovl_copy_up(ovl_cuw->dentry);
+
+	clear_bit(OVL_COPY_UP_PENDING, &ovl_cuw->flags);
+	wake_up_bit(&ovl_cuw->flags, OVL_COPY_UP_PENDING);
+}
+
 static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct file *realfile = file->private_data;
 	const struct cred *old_cred;
+	struct inode *inode = file->f_inode;
+	struct ovl_copy_up_work ovl_cuw;
+	DEFINE_WAIT_BIT(wait, &ovl_cuw.flags, OVL_COPY_UP_PENDING);
+	wait_queue_head_t *wqh;
 	int ret;
 
+	if (vma->vm_flags & MAP_SHARED &&
+			ovl_copy_up_shared(file_inode(file)->i_sb)) {
+		ovl_cuw.err = 0;
+		ovl_cuw.flags = 0;
+		ovl_cuw.dentry = file_dentry(file);
+		set_bit(OVL_COPY_UP_PENDING, &ovl_cuw.flags);
+
+		wqh = bit_waitqueue(&ovl_cuw.flags, OVL_COPY_UP_PENDING);
+		prepare_to_wait(wqh, &wait.wq_entry, TASK_UNINTERRUPTIBLE);
+
+		INIT_WORK(&ovl_cuw.work, ovl_copy_up_work_fn);
+		schedule_work(&ovl_cuw.work);
+
+		schedule();
+		finish_wait(wqh, &wait.wq_entry);
+
+		if (ovl_cuw.err)
+			return ovl_cuw.err;
+
+		realfile = ovl_open_realfile(file, ovl_inode_realdata(inode));
+		if (IS_ERR(realfile))
+			return PTR_ERR(realfile);
+
+		ovl_release(inode, file);
+		file->private_data = realfile;
+	}
+
 	if (!realfile->f_op->mmap)
 		return -ENODEV;
 
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 3623d28aa4fa..28853c18d59c 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -328,6 +328,12 @@ static inline void ovl_inode_unlock(struct inode *inode)
 	mutex_unlock(&OVL_I(inode)->lock);
 }
 
+static inline bool ovl_copy_up_shared(struct super_block *sb)
+{
+	struct ovl_fs *ofs = sb->s_fs_info;
+
+	return !(sb->s_flags & SB_RDONLY) && ofs->config.copy_up_shared;
+}
 
 /* namei.c */
 int ovl_check_fb_len(struct ovl_fb *fb, int fb_len);
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h
index 89015ea822e7..6007cafd2ac7 100644
--- a/fs/overlayfs/ovl_entry.h
+++ b/fs/overlayfs/ovl_entry.h
@@ -17,6 +17,7 @@ struct ovl_config {
 	bool nfs_export;
 	int xino;
 	bool metacopy;
+	bool copy_up_shared;
 };
 
 struct ovl_sb {
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 319fe0d355b0..35ed1aef3266 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -53,6 +53,12 @@ module_param_named(xino_auto, ovl_xino_auto_def, bool, 0644);
 MODULE_PARM_DESC(xino_auto,
 		 "Auto enable xino feature");
 
+static bool ovl_copy_up_shared_def =
+	IS_ENABLED(CONFIG_OVERLAY_FS_COPY_UP_SHARED);
+module_param_named(copy_up_shared, ovl_copy_up_shared_def, bool, 0644);
+MODULE_PARM_DESC(copy_up_shared,
+		 "Copy up when mapping a file shared");
+
 static void ovl_entry_stack_free(struct ovl_entry *oe)
 {
 	unsigned int i;
@@ -363,6 +369,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
 	if (ofs->config.metacopy != ovl_metacopy_def)
 		seq_printf(m, ",metacopy=%s",
 			   ofs->config.metacopy ? "on" : "off");
+	if (ofs->config.copy_up_shared != ovl_copy_up_shared_def)
+		seq_printf(m, ",copy_up_shared=%s",
+				ofs->config.copy_up_shared ? "on" : "off");
 	return 0;
 }
 
@@ -403,6 +412,8 @@ enum {
 	OPT_XINO_AUTO,
 	OPT_METACOPY_ON,
 	OPT_METACOPY_OFF,
+	OPT_COPY_UP_SHARED_ON,
+	OPT_COPY_UP_SHARED_OFF,
 	OPT_ERR,
 };
 
@@ -421,6 +432,8 @@ static const match_table_t ovl_tokens = {
 	{OPT_XINO_AUTO,			"xino=auto"},
 	{OPT_METACOPY_ON,		"metacopy=on"},
 	{OPT_METACOPY_OFF,		"metacopy=off"},
+	{OPT_COPY_UP_SHARED_ON,		"copy_up_shared=on"},
+	{OPT_COPY_UP_SHARED_OFF,	"copy_up_shared=off"},
 	{OPT_ERR,			NULL}
 };
 
@@ -559,6 +572,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
 			config->metacopy = false;
 			break;
 
+		case OPT_COPY_UP_SHARED_ON:
+			config->copy_up_shared = true;
+			break;
+
+		case OPT_COPY_UP_SHARED_OFF:
+			config->copy_up_shared = false;
+			break;
+
 		default:
 			pr_err("unrecognized mount option \"%s\" or missing value\n",
 					p);
@@ -1609,6 +1630,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 	ofs->config.nfs_export = ovl_nfs_export_def;
 	ofs->config.xino = ovl_xino_def();
 	ofs->config.metacopy = ovl_metacopy_def;
+	ofs->config.copy_up_shared = ovl_copy_up_shared_def;
 	err = ovl_parse_opt((char *) data, &ofs->config);
 	if (err)
 		goto out_err;
-- 
2.21.1







[Index of Archives]     [Linux Filesystems Devel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux