All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/10] Checkpoint/restart of open, unlinked files
@ 2011-03-01  4:05 Matt Helsley
       [not found] ` <1298952316-15763-1-git-send-email-matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
  0 siblings, 1 reply; 12+ messages in thread
From: Matt Helsley @ 2011-03-01  4:05 UTC (permalink / raw)
  To: Oren Laadan; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

This patch set implements the relink file operation and uses it to support
checkpoint and restart of open, unlinked files. During checkpoint,
sys_checkpoint relinks the files and returns. Userspace then checkpoints the
filesystem contents using any backup-like method prior to thawing. That
backup is then made available for use during an optional migration followed
by restore and sys_restart. In the case of network and cluster/distributed
filesystems copying the filesystem contents explicitly for migration may not
be necessary at all -- it would be part of normal file writes. For
non-migration uses of checkpoint/restart filesystems like btrfs a snapshot
could simply be taken during checkpoint and mounted during restart -- again
without requiring IO proportional to the aggregate size of filesystem
contents being checkpointed.

These IO savings are critical to the use of checkpoint/restart as a
fault mitigation solution in HPC environments where the probability of
component failure is very high simply due to the number of system
components. Incurring substantial IO for checkpoint/restart interferes
with the IO requirements of HPC jobs and thus reduces the frequency of
checkpoint/restart. That in turn means more processing time is lost
as a consequence of a fault -- the longer period between checkpoints
plus the IO required to re-establish hardlinks are simply not acceptable
for these environments.

Without relinking we would need to walk the entire filesystem to find out
that "b" is a path to the same inode (another variation on this case: "b"
would also have been unlinked). We'd need to do this for every
unlinked file that remains open in every task to checkpoint. Even then
there is no guarantee such a "b" exists for every unlinked file -- the
inodes could be "orphans" -- and we'd need to preserve their contents
some other way.

I considered a couple alternatives to preserving unlinked file contents:
copying and file handles. Each has significant drawbacks.

First I attempted to copy the file contents into the image and then
recreate and unlink the file during restart. Using a simple version of
that method the write above would not reach "b". One fix would be to search
the filesystem for a file with the same inode number (inode of "b") and
either open it or hardlink it to "a". Another would be to record the inode
number. This either shifts the search from checkpoint time to restart time
or has all the drawbacks of the second method I considered: file handles.

Instead of copying contents or recording inodes I also considered using
file handles. We'd need to ensure that the filehandles persist in storage,
can be snapshotted/backed up, and can be migrated. Can handlefs or any
generic file handle system do this? My _guess_ is "no" but folks are
welcome to tell me I'm wrong.

In contrast, linking the file from a_fd back into its filesystem can avoid
these complexities. Relinking avoids the search for matching inodes and
copying large quantities of data from storage only to write it back (in
fact a non-linking solution requires that the data be read-and-written
twice -- once for checkpoint and once for restart). Like file handles it does
require changes to the filesystem code. Unlike file handles, enabling
relinking does not require every filesystem to support a new kind of
filesystem "object" -- only an operation that is quite similar to one that
already exists: link.

[PATCH 01/10] Create the .relink file_operation
[PATCH 02/10] ext3/4: Allow relinking to unlinked files
[PATCH 03/10] Split do_linkat() out of sys_linkat
[PATCH 04/10] Checkpoint/restart unlinked files
[PATCH 05/10] Enable c/r of unlinked fifos
[PATCH 06/10] Support relinking unlinked files in btrfs
[PATCH 07/10] Add relink_dir superblock field
[PATCH 08/10] Parse the relink=%s mount option
[PATCH 09/10] Enabling checkpoint relink of unlinked files inside containers
[PATCH 10/10] [RFC] Use call_usermodehelper to cleanup after failure

BUGS:

	There's a memory leak (Reported-by: "Jose R. Santos"
<jrs-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>) that I haven't tracked down completely yet.
It seems to be in the "relink=" mount option parsing code -- I feel like I
must be missing some code path related to vfsmount handling.

Cheers,
	-Matt Helsley

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 01/10] Create the .relink file_operation
       [not found] ` <1298952316-15763-1-git-send-email-matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2011-03-01  4:05   ` Matt Helsley
       [not found]     ` <a6df3726c48b6f9e2422e9c12a32c4991bc53a25.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
  2011-03-01  5:05   ` [PATCH 00/10] Checkpoint/restart of open, unlinked files Matt Helsley
  1 sibling, 1 reply; 12+ messages in thread
From: Matt Helsley @ 2011-03-01  4:05 UTC (permalink / raw)
  To: Oren Laadan; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

Not all filesystems will necessarily be able to support relinking an
orphan inode back into the filesystem. Some offlist feedback suggested
that instead of overloading .link that relinking should be a separate
file operation for this reason.

Since .relink is a superset of .link make the VFS call .relink where
possible and .link otherwise.

The next commit will change ext3/4 to enable this operation.

Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
Cc: Theodore Ts'o <tytso-3s7WtUTddSA@public.gmane.org>
Cc: Andreas Dilger <adilger.kernel-m1MBpc4rdrD3fQ9qLvQP4Q@public.gmane.org>
Cc: Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
Cc: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-ext4-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Al Viro <viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
---
 fs/namei.c         |    5 ++++-
 include/linux/fs.h |    1 +
 2 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 4ff7ca5..b6b7359 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2430,7 +2430,10 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
 		return error;
 
 	mutex_lock(&inode->i_mutex);
-	error = dir->i_op->link(old_dentry, dir, new_dentry);
+	if (dir->i_op->relink)
+		error = dir->i_op->relink(old_dentry, dir, new_dentry);
+	else
+		error = dir->i_op->link(old_dentry, dir, new_dentry);
 	mutex_unlock(&inode->i_mutex);
 	if (!error)
 		fsnotify_link(dir, inode, new_dentry);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3d8bf75..af1cb0e 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1562,6 +1562,7 @@ struct inode_operations {
 	int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
 	struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
 	int (*link) (struct dentry *,struct inode *,struct dentry *);
+	int (*relink) (struct dentry *,struct inode *,struct dentry *);
 	int (*unlink) (struct inode *,struct dentry *);
 	int (*symlink) (struct inode *,struct dentry *,const char *);
 	int (*mkdir) (struct inode *,struct dentry *,int);
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 02/10] ext3/4: Allow relinking to unlinked files
       [not found]     ` <a6df3726c48b6f9e2422e9c12a32c4991bc53a25.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2011-03-01  4:05       ` Matt Helsley
       [not found]       ` <cbc5f629ca02554497bcb385039a2a300ef897f0.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
                         ` (7 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Matt Helsley @ 2011-03-01  4:05 UTC (permalink / raw)
  To: Oren Laadan; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

Orphan inodes (nlink == 0) have been forbidden from being linked back
into the ext3/4 filesystems as a means of dealing with a link/unlink
"race".

This patch effectively reverts 2988a7740dc0dd9a0cb56576e8fe1d777dff0db3

	"return ENOENT from ext3_link when racing with unlink"

which was discussed in the lkml thread:

	http://lkml.org/lkml/2007/1/12/200

The reverted commit was selected because disallowing relinking was just a
simpler solution -- not because removing tasks from the orphan list
was deemed difficult or incorrect.

Instead this patch utilizes the original patch proposed by Eric Sandeen.
Testing this patch with the orphan-repro.tar.bz2 code linked in that
thread seems to confirm that this patch does not reintroduce the OOPs.
Nonetheless, Amir Goldstein pointed out that if ext3_add_entry() fails
we'll also be left with a corrupted orphan list. So I've moved the orphan
removal code down to the spot where a successful return has been assured.

Eric's Original description (indented):

	Remove inode from the orphan list in ext3_link() if we might have
	raced with ext3_unlink(), which potentially put it on the list.
	If we're on the list with nlink > 0, we'll never get cleaned up
	properly and eventually may corrupt the list.

Unlike the reverted patch, this patch enables relinking an orphan inode
back into the filesystem. This relinking is extremely useful for
checkpoint/restart since it avoids incredibly costly and complex methods
of supporting checkpoint of tasks with open unlinked files. More on
how relink will be used can be found in the checkpoint/restart patches posted
with this patch.

Though relinking is a useful operation it may not be possible on all
filesystems. It appears to be possible in ext3/4 so this patch changes the
ext3/ext4 link functions to allow it and provide the .relink file operation
introduced earlier.

This assumes that in ext3/4 data written to unlinked files is not stored any
differently than data written to linked files. If that is not the case
I'd appreciate any pointers to the code showing where/how they are
handled differently. All I could find in ext3/4 was the orphan inode list.

Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
Cc: Eric Sandeen <sandeen-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Cc: Theodore Ts'o <tytso-3s7WtUTddSA@public.gmane.org>
Cc: Andreas Dilger <adilger.kernel-m1MBpc4rdrD3fQ9qLvQP4Q@public.gmane.org>
Cc: linux-ext4-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
Cc: Oren Laadan <orenl-eQaUEPhvms7ENvBUuze7eA@public.gmane.org>
Cc: Amir Goldstein <amir73il-iA+eEnwkJgzk1uMJSBkQmQ@public.gmane.org>
Cc: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Al Viro <viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
Cc: Christoph Hellwig <hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>
Cc: Jamie Lokier <jamie-yetKDKU6eevNLxjTenLetw@public.gmane.org>
---
 fs/ext3/namei.c |   16 ++++++++--------
 fs/ext4/namei.c |   14 +++++++-------
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c
index bce9dce..ceddffd 100644
--- a/fs/ext3/namei.c
+++ b/fs/ext3/namei.c
@@ -1984,7 +1984,7 @@ out_unlock:
 
 /*
  * ext3_orphan_del() removes an unlinked or truncated inode from the list
- * of such inodes stored on disk, because it is finally being cleaned up.
+ * of such inodes stored on disk.
  */
 int ext3_orphan_del(handle_t *handle, struct inode *inode)
 {
@@ -2242,13 +2242,6 @@ static int ext3_link (struct dentry * old_dentry,
 
 	dquot_initialize(dir);
 
-	/*
-	 * Return -ENOENT if we've raced with unlink and i_nlink is 0.  Doing
-	 * otherwise has the potential to corrupt the orphan inode list.
-	 */
-	if (inode->i_nlink == 0)
-		return -ENOENT;
-
 retry:
 	handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
 					EXT3_INDEX_EXTRA_TRANS_BLOCKS);
@@ -2266,6 +2259,12 @@ retry:
 	if (!err) {
 		ext3_mark_inode_dirty(handle, inode);
 		d_instantiate(dentry, inode);
+		if (inode->i_nlink == 1)
+			/*
+			 * i_nlink went from 0 to 1 thus the inode is no
+			 * longer an orphan.
+			 */
+			ext3_orphan_del(handle, inode);
 	} else {
 		drop_nlink(inode);
 		iput(inode);
@@ -2450,6 +2449,7 @@ end_rename:
 const struct inode_operations ext3_dir_inode_operations = {
 	.create		= ext3_create,
 	.lookup		= ext3_lookup,
+	.relink		= ext3_link,
 	.link		= ext3_link,
 	.unlink		= ext3_unlink,
 	.symlink	= ext3_symlink,
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index dc40e75..1bbc4c2 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2285,13 +2285,6 @@ static int ext4_link(struct dentry *old_dentry,
 
 	dquot_initialize(dir);
 
-	/*
-	 * Return -ENOENT if we've raced with unlink and i_nlink is 0.  Doing
-	 * otherwise has the potential to corrupt the orphan inode list.
-	 */
-	if (inode->i_nlink == 0)
-		return -ENOENT;
-
 retry:
 	handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
 					EXT4_INDEX_EXTRA_TRANS_BLOCKS);
@@ -2309,6 +2302,12 @@ retry:
 	if (!err) {
 		ext4_mark_inode_dirty(handle, inode);
 		d_instantiate(dentry, inode);
+		if (inode->i_nlink == 1)
+			/*
+			 * i_nlink went from 0 to 1 thus the inode is no
+			 * longer an orphan.
+			 */
+			ext4_orphan_del(handle, inode);
 	} else {
 		drop_nlink(inode);
 		iput(inode);
@@ -2497,6 +2496,7 @@ end_rename:
 const struct inode_operations ext4_dir_inode_operations = {
 	.create		= ext4_create,
 	.lookup		= ext4_lookup,
+	.relink		= ext4_link,
 	.link		= ext4_link,
 	.unlink		= ext4_unlink,
 	.symlink	= ext4_symlink,
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 03/10] Split do_linkat() out of sys_linkat
       [not found]       ` <cbc5f629ca02554497bcb385039a2a300ef897f0.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2011-03-01  4:05         ` Matt Helsley
  0 siblings, 0 replies; 12+ messages in thread
From: Matt Helsley @ 2011-03-01  4:05 UTC (permalink / raw)
  To: Oren Laadan; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

Separate the __user pathname handling from the bulk of the syscall.
Since we're doing this to enable relinking of unlinked files by
sys_checkpoint and not sys_linkat we're not using a sys-wrapper.

Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
Cc: Oren Laadan <orenl-eQaUEPhvms7ENvBUuze7eA@public.gmane.org>
Cc: Amir Goldstein <amir73il-iA+eEnwkJgzk1uMJSBkQmQ@public.gmane.org>
Cc: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Al Viro <viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
Cc: Christoph Hellwig <hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>
Cc: Jamie Lokier <jamie-yetKDKU6eevNLxjTenLetw@public.gmane.org>
---
 fs/namei.c |   77 +++++++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 50 insertions(+), 27 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index b6b7359..52aa274 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2440,6 +2440,51 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
 	return error;
 }
 
+/* If the file has been unlinked then old_dentry doesn't match old_path */
+static int do_linkat(struct path *old_path, struct dentry *old_dentry,
+		     struct nameidata *nd, int flags)
+{
+	struct dentry *new_dentry;
+	int error = -EXDEV;
+
+	if (old_path->mnt != nd->path.mnt)
+		goto out;
+	new_dentry = lookup_create(nd, 0);
+	error = PTR_ERR(new_dentry);
+	if (IS_ERR(new_dentry))
+		goto out_unlock;
+	error = mnt_want_write(nd->path.mnt);
+	if (error)
+		goto out_dput;
+	error = security_path_link(old_dentry, &nd->path, new_dentry);
+	if (error)
+		goto out_drop_write;
+	error = vfs_link(old_dentry, nd->path.dentry->d_inode, new_dentry);
+out_drop_write:
+	mnt_drop_write(nd->path.mnt);
+out_dput:
+	dput(new_dentry);
+out_unlock:
+	mutex_unlock(&nd->path.dentry->d_inode->i_mutex);
+out:
+	return error;
+}
+
+static int do_kern_linkat(struct path *old_path, struct dentry *old_dentry,
+			  int newdfd, const char * newname, int flags)
+{
+	struct nameidata nd;
+	int error;
+
+	error = do_path_lookup(newdfd, newname, LOOKUP_PARENT, &nd);
+	if (error)
+		goto out;
+	error = do_linkat(old_path, old_dentry, &nd, flags);
+	path_put(&nd.path);
+out:
+	return error;
+}
+
 /*
  * Hardlinks are often used in delicate situations.  We avoid
  * security-related surprises by not following symlinks on the
@@ -2452,7 +2497,6 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
 SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
 		int, newdfd, const char __user *, newname, int, flags)
 {
-	struct dentry *new_dentry;
 	struct nameidata nd;
 	struct path old_path;
 	int error;
@@ -2465,37 +2509,16 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
 			     flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
 			     &old_path);
 	if (error)
-		return error;
-
-	error = user_path_parent(newdfd, newname, &nd, &to);
-	if (error)
 		goto out;
-	error = -EXDEV;
-	if (old_path.mnt != nd.path.mnt)
-		goto out_release;
-	new_dentry = lookup_create(&nd, 0);
-	error = PTR_ERR(new_dentry);
-	if (IS_ERR(new_dentry))
-		goto out_unlock;
-	error = mnt_want_write(nd.path.mnt);
-	if (error)
-		goto out_dput;
-	error = security_path_link(old_path.dentry, &nd.path, new_dentry);
+	error = user_path_parent(newdfd, newname, &nd, &to);
 	if (error)
-		goto out_drop_write;
-	error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
-out_drop_write:
-	mnt_drop_write(nd.path.mnt);
-out_dput:
-	dput(new_dentry);
-out_unlock:
-	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
-out_release:
+		goto out_put_old_path;
+	error = do_linkat(&old_path, old_path.dentry, &nd, flags);
 	path_put(&nd.path);
 	putname(to);
-out:
+out_put_old_path:
 	path_put(&old_path);
-
+out:
 	return error;
 }
 
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 04/10] Checkpoint/restart unlinked files
       [not found]       ` <6f547f7b0dd12d409f993dba3fbc899dc52f2a78.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2011-03-01  4:05         ` Matt Helsley
  0 siblings, 0 replies; 12+ messages in thread
From: Matt Helsley @ 2011-03-01  4:05 UTC (permalink / raw)
  To: Oren Laadan; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

Implement checkpoint of unlinked files by relinking them into their
filesystem at:

	<fs root>/lost+found/checkpoint/<file>

Relinking offers many advantages over other means of checkpointing unlinked
files. It's offers substantial performance improvements by leveraging the
snapshotting capabilities of various linux block devices, filesystems, or
differential copying tools like rsync.

In addition to the original path of the file we save the newly-linked
path. This newly-linked path is opened during restart instead of the
original path.

To understand why relinking is extremely useful for checkpoint/restart
consider this simple pseudocode program and a specific example checkpoint
of it:

	a_fd = open("a"); /* example: size of the file at "a" is 1GB */
	link("a", "b");
	unlink("a");
	creat("a");
	             <---- example: checkpoint happens here
	write(a_fd, "bar");

The file "a" is unlinked and a different file has been placed at that
path. a_fd still refers to the inode shared with "b". When we restart
we must re-open the files such that writes to files opened via different
paths are visible. Using links makes this easy.

Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
Cc: Eric Sandeen <sandeen-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Cc: Theodore Ts'o <tytso-3s7WtUTddSA@public.gmane.org>
Cc: Andreas Dilger <adilger.kernel-m1MBpc4rdrD3fQ9qLvQP4Q@public.gmane.org>
Cc: linux-ext4-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
Cc: Oren Laadan <orenl-eQaUEPhvms7ENvBUuze7eA@public.gmane.org>
Cc: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Al Viro <viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
Cc: Christoph Hellwig <hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>
Cc: Jamie Lokier <jamie-yetKDKU6eevNLxjTenLetw@public.gmane.org>
Cc: Amir Goldstein <amir73il-iA+eEnwkJgzk1uMJSBkQmQ@public.gmane.org>
Cc: Aneesh Kumar <aneesh.kumar-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
Cc: Miklos Szeredi <miklos-sUDqSbJrdHQHWmgEVkV9KA@public.gmane.org>
---
 fs/checkpoint.c                  |   85 ++++++++++++++++++----
 fs/namei.c                       |  148 ++++++++++++++++++++++++++++++++++++++
 fs/pipe.c                        |    2 +-
 include/linux/checkpoint.h       |    3 +-
 include/linux/checkpoint_hdr.h   |    3 +
 include/linux/checkpoint_types.h |    6 ++
 kernel/checkpoint/sys.c          |   12 +++
 7 files changed, 243 insertions(+), 16 deletions(-)

diff --git a/fs/checkpoint.c b/fs/checkpoint.c
index fd539c5..94a2eb4 100644
--- a/fs/checkpoint.c
+++ b/fs/checkpoint.c
@@ -16,6 +16,7 @@
 #include <linux/sched.h>
 #include <linux/file.h>
 #include <linux/namei.h>
+#include <linux/mount.h>
 #include <linux/fs_struct.h>
 #include <linux/fs.h>
 #include <linux/fdtable.h>
@@ -27,6 +28,7 @@
 #include <linux/checkpoint.h>
 #include <linux/eventpoll.h>
 #include <linux/eventfd.h>
+#include <linux/sys-wrapper.h>
 #include <net/sock.h>
 
 /**************************************************************************
@@ -173,6 +175,9 @@ int checkpoint_file_common(struct ckpt_ctx *ctx, struct file *file,
 	h->f_pos = file->f_pos;
 	h->f_version = file->f_version;
 
+	if (d_unlinked(file->f_dentry))
+		/* Perform post-checkpoint and post-restart unlink() */
+		h->f_restart_flags |= CKPT_RESTART_FILE_F_UNLINK;
 	h->f_credref = checkpoint_obj(ctx, f_cred, CKPT_OBJ_CRED);
 	if (h->f_credref < 0)
 		return h->f_credref;
@@ -196,16 +201,6 @@ int generic_file_checkpoint(struct ckpt_ctx *ctx, struct file *file)
 	struct ckpt_hdr_file_generic *h;
 	int ret;
 
-	/*
-	 * FIXME: when we'll add support for unlinked files/dirs, we'll
-	 * need to distinguish between unlinked filed and unlinked dirs.
-	 */
-	if (d_unlinked(file->f_dentry)) {
-		ckpt_err(ctx, -EBADF, "%(T)%(P)Unlinked files unsupported\n",
-			 file);
-		return -EBADF;
-	}
-
 	h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_FILE);
 	if (!h)
 		return -ENOMEM;
@@ -219,6 +214,9 @@ int generic_file_checkpoint(struct ckpt_ctx *ctx, struct file *file)
 	if (ret < 0)
 		goto out;
 	ret = checkpoint_fname(ctx, &file->f_path, &ctx->root_fs_path);
+	if (ret < 0)
+		goto out;
+	ret = checkpoint_file_links(ctx, file);
  out:
 	ckpt_hdr_put(ctx, h);
 	return ret;
@@ -566,12 +564,45 @@ static int ckpt_read_fname(struct ckpt_ctx *ctx, char **fname)
 	return len;
 }
 
+struct dq_unlink_entry {
+	struct ckpt_ctx *ctx;
+	char *fname;
+	bool do_rmdir;
+};
+
+/* Restart failed -- don't unlink fs contents */
+static int restore_dq_dont_unlink(void *dq_data)
+{
+	struct dq_unlink_entry *entry = dq_data;
+
+	kfree(entry->fname);
+	return 0;
+}
+
+/* Restart succeeded -- unlink fd contents */
+static int restore_dq_unlink(void *dq_data)
+{
+	struct dq_unlink_entry *entry = dq_data;
+	int ret;
+
+	if (entry->do_rmdir)
+		ret = kernel_sys_rmdir(entry->fname);
+	else
+		ret = kernel_sys_unlink(entry->fname);
+	if (ret < 0)
+		ckpt_err(entry->ctx, ret, "Could not unlink \"%s\"\n", entry->fname);
+	kfree(entry->fname);
+	return 0;
+}
+
 /**
  * restore_open_fname - read a file name and open a file
  * @ctx: checkpoint context
+ * @do_unlink: unlink the opened file
  * @flags: file flags
  */
-struct file *restore_open_fname(struct ckpt_ctx *ctx, int flags)
+struct file *restore_open_fname(struct ckpt_ctx *ctx,
+				int do_unlink, int flags)
 {
 	struct file *file;
 	char *fname;
@@ -585,9 +616,35 @@ struct file *restore_open_fname(struct ckpt_ctx *ctx, int flags)
 	if (len < 0)
 		return ERR_PTR(len);
 	ckpt_debug("fname '%s' flags %#x\n", fname, flags);
-
+	if (do_unlink) {
+		kfree(fname);
+		fname = NULL;
+		len = ckpt_read_payload(ctx, (void **)&fname, PATH_MAX,
+					CKPT_HDR_BUFFER);
+		if (len < 0)
+			return ERR_PTR(len);
+		fname[len] = '\0';
+	}
 	file = filp_open(fname, flags, 0);
-	kfree(fname);
+	if (IS_ERR(file)) {
+		ckpt_err(ctx, PTR_ERR(file), "Could not open file \"%s\"\n", fname);
+
+		goto out;
+	}
+	if (do_unlink) {
+		struct dq_unlink_entry entry;
+
+		/* Don't unlink if restart fails. Conversely, only unlink
+		 * if restart succeeds. */
+		ckpt_debug("deferring unlinking of \"%s\"\n", fname);
+		entry.ctx = ctx;
+		entry.do_rmdir = S_ISDIR(file->f_mapping->host->i_mode);
+		entry.fname = fname;
+		deferqueue_add(ctx->err_deferq, &entry, sizeof(entry),
+			       restore_dq_dont_unlink, restore_dq_unlink);
+	} else
+out:
+		kfree(fname);
 
 	return file;
 }
@@ -691,7 +748,7 @@ static struct file *generic_file_restore(struct ckpt_ctx *ctx,
 	    ptr->h.len != sizeof(*ptr) || ptr->f_type != CKPT_FILE_GENERIC)
 		return ERR_PTR(-EINVAL);
 
-	file = restore_open_fname(ctx, ptr->f_flags);
+	file = restore_open_fname(ctx, !!(ptr->f_restart_flags & CKPT_RESTART_FILE_F_UNLINK), ptr->f_flags);
 	if (IS_ERR(file))
 		return file;
 
diff --git a/fs/namei.c b/fs/namei.c
index 52aa274..6dea3b1 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -32,6 +32,11 @@
 #include <linux/fcntl.h>
 #include <linux/device_cgroup.h>
 #include <linux/fs_struct.h>
+#ifdef CONFIG_CHECKPOINT
+#include <linux/sys-wrapper.h>
+#include <linux/deferqueue.h>
+#include <linux/checkpoint.h>
+#endif
 #include <asm/uaccess.h>
 
 #include "internal.h"
@@ -2527,6 +2532,149 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
 	return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
 }
 
+#ifdef CONFIG_CHECKPOINT
+
+/* Path relative to the mounted filesystem's root -- not a "global" root or even a namespace root. The unique_name_count is unique for the entire checkpoint. */
+#define CKPT_RELINKAT_FMT "lost+found/checkpoint-%d/relinked-%u"
+
+static int checkpoint_fill_relink_fname(struct ckpt_ctx *ctx,
+					struct file *for_file,
+					char relink_dir_pathname[PATH_MAX],
+					int *lenp)
+{
+	struct path relink_dir_path;
+	char *tmp;
+	int len;
+
+	/* Find path to mount */
+	relink_dir_path.mnt = for_file->f_path.mnt;
+	relink_dir_path.dentry = relink_dir_path.mnt->mnt_root;
+	tmp = d_path(&relink_dir_path, relink_dir_pathname, PATH_MAX);
+	if (IS_ERR(tmp))
+		return PTR_ERR(tmp);
+
+	/* Append path to relinked file. */
+	len = strlen(tmp);
+	if (len <= 0)
+		return -ENOENT;
+	memmove(relink_dir_pathname, tmp, len);
+	tmp = relink_dir_pathname + len - 1;
+	/* Ensure we've got a single dir separator */
+	if (*tmp == '/')
+		tmp++;
+	else {
+		tmp++;
+		*tmp = '/';
+		tmp++;
+		len++;
+	}
+	len += snprintf(tmp, PATH_MAX - len, CKPT_RELINKAT_FMT,
+			ctx->crid, ++ctx->unique_name_count);
+	relink_dir_pathname[len] = '\0';
+	*lenp = len;
+	return 0;
+}
+
+/*
+ * Transform path to path with last element removed. Changes contents of path.
+ */
+static void mkdirname(char *path, char **delim_p)
+{
+	char *p = path;
+
+	if (delim_p)
+		*delim_p = NULL;
+	while (*p)
+		p++;
+	while (p > path && *p != '/')
+		p--;
+	if (*p == '/') {
+		*p = '\0';
+		if (delim_p)
+			*delim_p = p;
+	}
+}
+
+/*
+ * Make the directory used to collect all of the links in. Remove it
+ * if checkpoint fails.
+ */
+static int checkpoint_make_relink_collection(struct ckpt_ctx *ctx,
+					     char *new_path,
+					     int len)
+{
+	char *delim;
+	int ret;
+
+	mkdirname(new_path, &delim);
+	if (!delim || (strlen(new_path) < 2)) /* Need a non-empty dirname */
+		return -ENOENT;
+	ret = kernel_sys_mkdir(new_path, S_IRWXU & ~current_umask());
+	if (delim)
+		*delim = '/';
+	if (ret == -EEXIST) /* already created the collection dir */
+		ret = 0;
+	return ret;
+}
+
+static int checkpoint_file_relink(struct ckpt_ctx *ctx,
+				  struct file *file,
+				  char new_path[PATH_MAX])
+{
+	int ret, len;
+
+	/* 
+	 * Relinking arbitrary files without searching a path
+	 * (which is non-existent if the file is unlinked) requires
+	 * special privileges.
+	 */
+	if (!capable(CAP_DAC_OVERRIDE|CAP_DAC_READ_SEARCH)) {
+		ckpt_err(ctx, -EPERM, "%(T)Relinking unlinked files requires CAP_DAC_{OVERRIDE,READ_SEARCH}\n");
+		return -EPERM;
+	}
+	ret = checkpoint_fill_relink_fname(ctx, file, new_path, &len);
+	if (ret)
+		return ret;
+	ret = checkpoint_make_relink_collection(ctx, new_path, len);
+	if (ret)
+		return ret;
+	ret = do_kern_linkat(&file->f_path, file->f_dentry,
+			     AT_FDCWD, new_path, 0);
+	if (ret)
+		ckpt_err(ctx, ret, "%(T)%(P)%(V)Failed to relink unlinked file.\n", file, file->f_op);
+	return ret;
+}
+
+int checkpoint_file_links(struct ckpt_ctx *ctx, struct file *file)
+{
+	char *new_link_path;
+	int ret, len;
+
+	if (!d_unlinked(file->f_dentry))
+		return 0;
+
+	/*
+	 * Unlinked files need at least one hardlink for the post-sys_checkpoint
+	 * filesystem backup/snapshot.
+	 */
+	new_link_path = kmalloc(PATH_MAX, GFP_KERNEL);
+	if (!new_link_path)
+		return -ENOMEM;
+	ret = checkpoint_file_relink(ctx, file, new_link_path);
+	if (ret < 0)
+		goto out_free;
+	len = strlen(new_link_path);
+	ret = ckpt_write_obj_type(ctx, NULL, len + 1, CKPT_HDR_BUFFER);
+	if (ret < 0)
+		goto out_free;
+	ret = ckpt_kwrite(ctx, new_link_path, len + 1);
+out_free:
+	kfree(new_link_path);
+
+	return ret;
+}
+#endif /* CONFIG_CHECKPOINT */
+
 /*
  * The worst of all namespace operations - renaming directory. "Perverted"
  * doesn't even start to describe it. Somebody in UCB had a heck of a trip...
diff --git a/fs/pipe.c b/fs/pipe.c
index d79ad44..e66ba97 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1053,7 +1053,7 @@ struct file *fifo_file_restore(struct ckpt_ctx *ctx, struct ckpt_hdr_file *ptr)
 	 * To avoid blocking, always open the fifo with O_RDWR;
 	 * then fix flags below.
 	 */
-	file = restore_open_fname(ctx, (ptr->f_flags & ~O_ACCMODE) | O_RDWR);
+	file = restore_open_fname(ctx, 0, (ptr->f_flags & ~O_ACCMODE) | O_RDWR);
 	if (IS_ERR(file))
 		return file;
 
diff --git a/include/linux/checkpoint.h b/include/linux/checkpoint.h
index 6c0ccfd..8686ce7 100644
--- a/include/linux/checkpoint.h
+++ b/include/linux/checkpoint.h
@@ -257,7 +257,8 @@ extern int restore_obj_file_table(struct ckpt_ctx *ctx, int files_objref);
 /* files */
 extern int checkpoint_fname(struct ckpt_ctx *ctx,
 			    struct path *path, struct path *root);
-extern struct file *restore_open_fname(struct ckpt_ctx *ctx, int flags);
+extern int checkpoint_file_links(struct ckpt_ctx *ctx, struct file *file);
+extern struct file *restore_open_fname(struct ckpt_ctx *ctx, int restore_unlinked, int flags);
 
 extern int ckpt_collect_file(struct ckpt_ctx *ctx, struct file *file);
 
diff --git a/include/linux/checkpoint_hdr.h b/include/linux/checkpoint_hdr.h
index bb5a749..fdc4884 100644
--- a/include/linux/checkpoint_hdr.h
+++ b/include/linux/checkpoint_hdr.h
@@ -607,6 +607,9 @@ struct ckpt_hdr_file {
 	__u64 f_pos;
 	__u64 f_version;
 	__s32 f_secref;
+
+	__u32 f_restart_flags;
+#define CKPT_RESTART_FILE_F_UNLINK (1<<0)
 } __attribute__((aligned(8)));
 
 struct ckpt_hdr_file_generic {
diff --git a/include/linux/checkpoint_types.h b/include/linux/checkpoint_types.h
index cf74d3e..7d14676 100644
--- a/include/linux/checkpoint_types.h
+++ b/include/linux/checkpoint_types.h
@@ -61,6 +61,12 @@ struct ckpt_ctx {
 	struct completion errno_sync;	/* protect errno setting */
 	int errno;			/* errno that caused failure */
 
+	/* relink unlinked files to <mnt_root>/<unique_name> */
+	unsigned int unique_name_count;
+	struct deferqueue_head *err_deferq; /* Deferred cleanup if hit error.
+					     * Run after finalize.
+					     */
+
 	struct list_head pgarr_list;	/* page array to dump VMA contents */
 	struct list_head pgarr_pool;	/* pool of empty page arrays chain */
 	void *scratch_page;             /* scratch buffer for page I/O */
diff --git a/kernel/checkpoint/sys.c b/kernel/checkpoint/sys.c
index 2383db9..a15d37a 100644
--- a/kernel/checkpoint/sys.c
+++ b/kernel/checkpoint/sys.c
@@ -258,6 +258,13 @@ static void ckpt_ctx_free(struct ckpt_ctx *ctx)
 	sock_listening_list_free(&ctx->listen_sockets);
 #endif
 
+	/* cleanup after error(s) */
+	if (ctx->err_deferq) {
+		if (ckpt_test_error(ctx))
+			deferqueue_run(ctx->err_deferq);
+		deferqueue_destroy(ctx->err_deferq);
+	}
+
 	kfree(ctx);
 }
 
@@ -306,8 +313,13 @@ static struct ckpt_ctx *ckpt_ctx_alloc(int fd, unsigned long uflags,
 
  nolog:
 	err = -ENOMEM;
+	ctx->err_deferq = deferqueue_create();
+	if (!ctx->err_deferq)
+		goto err;
+
 	if (ckpt_obj_hash_alloc(ctx) < 0)
 		goto err;
+
 	ctx->deferqueue = deferqueue_create();
 	if (!ctx->deferqueue)
 		goto err;
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 05/10] Enable c/r of unlinked fifos
       [not found]       ` <1a7a344ddd1d68ac862ec2248be77f2dd9f138c8.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2011-03-01  4:05         ` Matt Helsley
  0 siblings, 0 replies; 12+ messages in thread
From: Matt Helsley @ 2011-03-01  4:05 UTC (permalink / raw)
  To: Oren Laadan; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

Unlinked fifos are special files which share some of their
checkpoint/restart code with pipes. Re-use the code for normal
unlinked files for unlinked fifos too.

Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
 fs/pipe.c                  |    5 ++++-
 include/linux/checkpoint.h |    1 +
 2 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/fs/pipe.c b/fs/pipe.c
index e66ba97..e9f3e64 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -917,6 +917,9 @@ static int pipe_file_checkpoint(struct ckpt_ctx *ctx, struct file *file)
 		ret = checkpoint_fname(ctx, &file->f_path, &ctx->root_fs_path);
 		if (ret < 0)
 			goto out;
+		ret = checkpoint_file_links(ctx, file);
+		if (ret < 0)
+			goto out;
 	}
 
 	if (first)
@@ -1053,7 +1056,7 @@ struct file *fifo_file_restore(struct ckpt_ctx *ctx, struct ckpt_hdr_file *ptr)
 	 * To avoid blocking, always open the fifo with O_RDWR;
 	 * then fix flags below.
 	 */
-	file = restore_open_fname(ctx, 0, (ptr->f_flags & ~O_ACCMODE) | O_RDWR);
+	file = restore_open_fname(ctx, !!(ptr->f_restart_flags & CKPT_RESTART_FILE_F_UNLINK), (ptr->f_flags & ~O_ACCMODE) | O_RDWR);
 	if (IS_ERR(file))
 		return file;
 
diff --git a/include/linux/checkpoint.h b/include/linux/checkpoint.h
index 8686ce7..f31671f 100644
--- a/include/linux/checkpoint.h
+++ b/include/linux/checkpoint.h
@@ -109,6 +109,7 @@ extern int ckpt_read_consume(struct ckpt_ctx *ctx, int len, int type);
 
 extern char *ckpt_fill_fname(struct path *path, struct path *root,
 			     char *buf, int *len);
+extern int checkpoint_file_links(struct ckpt_ctx *ctx, struct file *file);
 
 extern int checkpoint_dump_page(struct ckpt_ctx *ctx, struct page *page);
 extern int restore_read_page(struct ckpt_ctx *ctx, struct page *page);
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 06/10] Support relinking unlinked files in btrfs
       [not found]       ` <dbe36642be44152833a63e8173e8f0490dc1573f.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2011-03-01  4:05         ` Matt Helsley
  0 siblings, 0 replies; 12+ messages in thread
From: Matt Helsley @ 2011-03-01  4:05 UTC (permalink / raw)
  To: Oren Laadan; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
Cc: Chris Mason <chris.mason-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org>
Cc: linux-btrfs-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
---
 fs/btrfs/inode.c |   17 +++++++++++++----
 1 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index bb5064b..ce923eb 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4776,14 +4776,15 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
 	unsigned long nr = 0;
 	int err;
 	int drop_inode = 0;
-
-	if (inode->i_nlink == 0)
-		return -ENOENT;
+	int adopted = 0;
 
 	/* do not allow sys_link's with other subvols of the same device */
 	if (root->objectid != BTRFS_I(inode)->root->objectid)
 		return -EPERM;
 
+	if (inode->i_nlink == 0)
+		/* Not orphaned by truncate -- getting back in the game! */
+		adopted = 1;
 	btrfs_inc_nlink(inode);
 	inode->i_ctime = CURRENT_TIME;
 
@@ -4804,7 +4805,15 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
 	btrfs_set_trans_block_group(trans, dir);
 	ihold(inode);
 
-	err = btrfs_add_nondir(trans, dir, dentry, inode, 1, index);
+	if (adopted) {
+		/* inode->i_nlink was 0 -- we were an orphan */
+		err = btrfs_orphan_del(trans, inode);
+		if (err)
+			drop_inode = 1;
+	}
+
+	if (!err)
+		err = btrfs_add_nondir(trans, dir, dentry, inode, 1, index);
 
 	if (err) {
 		drop_inode = 1;
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 07/10] Add relink_dir superblock field
       [not found]       ` <ac2a9431a97bbb45bdecf2ccd011f70ee99f2731.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2011-03-01  4:05         ` Matt Helsley
  0 siblings, 0 replies; 12+ messages in thread
From: Matt Helsley @ 2011-03-01  4:05 UTC (permalink / raw)
  To: Oren Laadan; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

This patch adds the pointer for a relink mount(8) option which specifies a
path to a directory within the given filesystem where checkpoint/restart
may relink files. If the option is unset then checkpoint/restart tries
to use "lost+found".

A subsequent patch will enable userspace to set a relink=/foo/bar
option when mounting a filesystem.

Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
 fs/namei.c         |    6 ++++--
 fs/namespace.c     |    6 ++++++
 include/linux/fs.h |    2 ++
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 6dea3b1..fcf35b3 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2535,7 +2535,7 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
 #ifdef CONFIG_CHECKPOINT
 
 /* Path relative to the mounted filesystem's root -- not a "global" root or even a namespace root. The unique_name_count is unique for the entire checkpoint. */
-#define CKPT_RELINKAT_FMT "lost+found/checkpoint-%d/relinked-%u"
+#define CKPT_RELINKAT_FMT "%s/checkpoint-%d/relinked-%u"
 
 static int checkpoint_fill_relink_fname(struct ckpt_ctx *ctx,
 					struct file *for_file,
@@ -2569,7 +2569,9 @@ static int checkpoint_fill_relink_fname(struct ckpt_ctx *ctx,
 		len++;
 	}
 	len += snprintf(tmp, PATH_MAX - len, CKPT_RELINKAT_FMT,
-			ctx->crid, ++ctx->unique_name_count);
+			relink_dir_path.mnt->mnt_sb->s_relink_dir ? relink_dir_path.mnt->mnt_sb->s_relink_dir : "lost+found",
+			ctx->crid,
+			 ++ctx->unique_name_count);
 	relink_dir_pathname[len] = '\0';
 	*lenp = len;
 	return 0;
diff --git a/fs/namespace.c b/fs/namespace.c
index 8c83cd3..31fe15f 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -810,6 +810,12 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb)
 		if (sb->s_flags & fs_infop->flag)
 			seq_puts(m, fs_infop->str);
 	}
+#ifdef CONFIG_CHECKPOINT
+	if (sb->s_relink_dir) {
+		seq_puts(m, ",relink=");
+		seq_puts(m, sb->s_relink_dir);
+	}
+#endif /* CONFIG_CHECKPOINT */
 
 	return security_sb_show_options(m, sb);
 }
diff --git a/include/linux/fs.h b/include/linux/fs.h
index af1cb0e..826df6f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1426,6 +1426,8 @@ struct super_block {
 	 * generic_show_options()
 	 */
 	char __rcu *s_options;
+
+	char *s_relink_dir;
 };
 
 extern struct timespec current_fs_time(struct super_block *sb);
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 08/10] Parse the relink=%s mount option
       [not found]       ` <24a5e0d9179d6e3c29ec0197a3251963ff2a4dbd.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2011-03-01  4:05         ` Matt Helsley
  0 siblings, 0 replies; 12+ messages in thread
From: Matt Helsley @ 2011-03-01  4:05 UTC (permalink / raw)
  To: Oren Laadan; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

Add a generic string mount option for relinking during checkpoint/restart.
It can be passed via mount commands. The specified path is relative to the
filesystem root and must remain within the filesystem being mounted.

Use of this mount option looks like (... for the uninteresting bits):

	mount ... -o ...,relink="qux/quux/" ...

Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
 fs/super.c         |   77 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/fs.h |    2 +
 2 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/fs/super.c b/fs/super.c
index ca69615..2bda808 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -127,6 +127,9 @@ static inline void destroy_super(struct super_block *s)
 	free_percpu(s->s_files);
 #endif
 	security_sb_free(s);
+#ifdef CONFIG_CHECKPOINT
+	kfree(s->s_relink_dir);
+#endif
 	kfree(s->s_subtype);
 	kfree(s->s_options);
 	kfree(s);
@@ -952,12 +955,58 @@ int get_sb_single(struct file_system_type *fs_type,
 
 EXPORT_SYMBOL(get_sb_single);
 
+#ifdef CONFIG_CHECKPOINT
+static int parse_relink_opt(char *data, char *result, int max_result_len)
+{
+	char *s = data;
+	char *next;
+	char *opt_start;
+	int len = 0;
+
+	while (*s != '\0') {
+		next = strchr(s, ',');
+		if (!next) {
+			next = s + strlen(s);
+			len = next - s;
+		} else {
+			len = next - s;
+			next++;
+		}
+		if (strncmp(s, "relink=", 7)) {
+			s = next;
+			len = 0;
+			continue;
+		}
+		opt_start = s;
+		len -= 7;
+		s += 7;
+
+		/* Extract the relink=["]/path/to/foo["] option */
+		if (*s == '"' && s[len - 1] == '"') {
+			s++;
+			len -= 2;
+		}
+		len = max(min(len, max_result_len), 0);
+		strncpy(result, s, len);
+
+		/* Erase the relink= option */
+		memmove(opt_start, next, strlen(next) + 1);
+		break;
+	}
+	result[len] = '\0';
+	return len;
+}
+#endif
+
 struct vfsmount *
 vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
 {
 	struct vfsmount *mnt;
 	struct dentry *root;
 	char *secdata = NULL;
+#ifdef CONFIG_CHECKPOINT
+	char *relink_dir;
+#endif
 	int error;
 
 	if (!type)
@@ -970,16 +1019,33 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
 
 	if (flags & MS_KERNMOUNT)
 		mnt->mnt_flags = MNT_INTERNAL;
-
+#ifdef CONFIG_CHECKPOINT
+	relink_dir = kmalloc(PATH_MAX, GFP_KERNEL);
+	if (!relink_dir)
+		goto out_mnt;
+	*relink_dir = '\0';
+#endif
 	if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
 		secdata = alloc_secdata();
 		if (!secdata)
-			goto out_mnt;
+			goto out_relink_dir;
 
 		error = security_sb_copy_data(data, secdata);
 		if (error)
 			goto out_free_secdata;
+#ifdef CONFIG_CHECKPOINT
+		if (parse_relink_opt(data, relink_dir, PATH_MAX) < 1) {
+			kfree(relink_dir);
+			relink_dir = NULL;
+		}
+#endif
+	}
+#ifdef CONFIG_CHECKPOINT
+	else {
+		kfree(relink_dir);
+		relink_dir = NULL;
 	}
+#endif
 
 	if (type->mount) {
 		root = type->mount(type, flags, name, data);
@@ -1014,6 +1080,9 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
 
 	mnt->mnt_mountpoint = mnt->mnt_root;
 	mnt->mnt_parent = mnt;
+#ifdef CONFIG_CHECKPOINT
+	mnt->mnt_sb->s_relink_dir = relink_dir;
+#endif
 	up_write(&mnt->mnt_sb->s_umount);
 	free_secdata(secdata);
 	return mnt;
@@ -1022,7 +1091,11 @@ out_sb:
 	deactivate_locked_super(mnt->mnt_sb);
 out_free_secdata:
 	free_secdata(secdata);
+out_relink_dir:
+#ifdef CONFIG_CHECKPOINT
+	kfree(relink_dir);
 out_mnt:
+#endif
 	free_vfsmnt(mnt);
 out:
 	return ERR_PTR(error);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 826df6f..ab97ceb 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1427,7 +1427,9 @@ struct super_block {
 	 */
 	char __rcu *s_options;
 
+#ifdef CONFIG_CHECKPOINT
 	char *s_relink_dir;
+#endif
 };
 
 extern struct timespec current_fs_time(struct super_block *sb);
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 09/10] Enabling checkpoint relink of unlinked files inside containers
       [not found]       ` <3d0a3c702e5986a45676987a4f3a6607d28725e7.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2011-03-01  4:05         ` Matt Helsley
  0 siblings, 0 replies; 12+ messages in thread
From: Matt Helsley @ 2011-03-01  4:05 UTC (permalink / raw)
  To: Oren Laadan; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

When we use relinking to checkpoint unlinked files we're often working
in a different mount namespace than the opened file. This means that
we'll get -EXDEV because the mounts are different and there is no
reasonable way to map between them in different namespaces.

Factor setns() and utilize it to quickly switch namespaces so that
we may relink files in different mount namespaces during checkpoint.

Unlike setns() we already have the mount namespace pointer and know
the proc namespace ops to use -- so we can skip a few steps and call
a factored kern_setns() in both the setns() syscall and from
checkpoint.

Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
 fs/namei.c              |   40 +++++++++++++++++++++++++++++++++-------
 include/linux/nsproxy.h |    3 +++
 kernel/nsproxy.c        |   33 +++++++++++++++++++--------------
 3 files changed, 55 insertions(+), 21 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index fcf35b3..f6361f8 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -33,6 +33,8 @@
 #include <linux/device_cgroup.h>
 #include <linux/fs_struct.h>
 #ifdef CONFIG_CHECKPOINT
+#include <linux/proc_fs.h>
+#include <linux/nsproxy.h>
 #include <linux/sys-wrapper.h>
 #include <linux/deferqueue.h>
 #include <linux/checkpoint.h>
@@ -2623,27 +2625,51 @@ static int checkpoint_file_relink(struct ckpt_ctx *ctx,
 				  struct file *file,
 				  char new_path[PATH_MAX])
 {
+	struct nsproxy *old_nsproxy;
 	int ret, len;
 
 	/* 
 	 * Relinking arbitrary files without searching a path
 	 * (which is non-existent if the file is unlinked) requires
-	 * special privileges.
+	 * special privileges. Also, since we need to set the mount
+	 * namespace we need CAP_SYS_ADMIN (see sys_setns).
 	 */
-	if (!capable(CAP_DAC_OVERRIDE|CAP_DAC_READ_SEARCH)) {
-		ckpt_err(ctx, -EPERM, "%(T)Relinking unlinked files requires CAP_DAC_{OVERRIDE,READ_SEARCH}\n");
+	if (!capable(CAP_DAC_OVERRIDE|CAP_DAC_READ_SEARCH) || !capable(CAP_SYS_ADMIN)) {
+		ckpt_err(ctx, -EPERM, "%(T)Relinking unlinked files requires CAP_DAC_{OVERRIDE,READ_SEARCH} and CAP_SYS_ADMIN\n");
 		return -EPERM;
 	}
-	ret = checkpoint_fill_relink_fname(ctx, file, new_path, &len);
-	if (ret)
+
+	/* Temporarily set mount namespace to be that of the file to 'relink' */
+	old_nsproxy = current->nsproxy;
+	get_nsproxy(old_nsproxy);
+	ret = kern_setns(&mntns_operations, file->f_path.mnt->mnt_ns);
+	if (ret) {
+		/* We never switched so we never dropped the new ref to old_nsproxy*/
+		put_nsproxy(old_nsproxy);
 		return ret;
+	}
+
+	/* The switch dropped the old ref to old_nsproxy */
+
+	/* Relink it */
+	ret = checkpoint_fill_relink_fname(ctx, file, new_path, &len);
+	if (ret) {
+		ckpt_err(ctx, ret, "%(T)%(P)%(V)Failed to fill relink name.\n", file, file->f_op);
+		goto switch_oldns;
+	}
 	ret = checkpoint_make_relink_collection(ctx, new_path, len);
-	if (ret)
-		return ret;
+	if (ret) {
+		ckpt_err(ctx, ret, "%(T)%(P)%(V)Failed to make relink collection dir for \"%s\".\n", file, file->f_op, new_path);
+		goto switch_oldns;
+	}
 	ret = do_kern_linkat(&file->f_path, file->f_dentry,
 			     AT_FDCWD, new_path, 0);
 	if (ret)
 		ckpt_err(ctx, ret, "%(T)%(P)%(V)Failed to relink unlinked file.\n", file, file->f_op);
+
+	/* Restore old mount namespace */
+switch_oldns:
+	switch_task_namespaces(current, old_nsproxy);
 	return ret;
 }
 
diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h
index 7b370c7..adcef23 100644
--- a/include/linux/nsproxy.h
+++ b/include/linux/nsproxy.h
@@ -69,6 +69,9 @@ void free_nsproxy(struct nsproxy *ns);
 int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **,
 	struct fs_struct *);
 
+struct proc_ns_operations;
+int kern_setns(const struct proc_ns_operations *ops, void *ns);
+
 static inline void put_nsproxy(struct nsproxy *ns)
 {
 	if (atomic_dec_and_test(&ns->count)) {
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index 62ee344..5c7c59a 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -454,11 +454,27 @@ static inline int checkpoint_register_nsproxy(void)
 }
 #endif /* CONFIG_CHECKPOINT */
 
+int kern_setns(const struct proc_ns_operations *ops, void *ns)
+{
+	struct nsproxy *new_nsproxy;
+	int err;
+
+	new_nsproxy = create_new_namespaces(0, current, current->fs);
+	if (IS_ERR(new_nsproxy))
+		return PTR_ERR(new_nsproxy);
+	err = ops->install(new_nsproxy, ns);
+	if (err) {
+		free_nsproxy(new_nsproxy);
+		goto out;
+	}
+	switch_task_namespaces(current, new_nsproxy);
+out:
+	return err;
+}
+
 SYSCALL_DEFINE2(setns, unsigned int, nstype, int, fd)
 {
 	const struct proc_ns_operations *ops;
-	struct task_struct *tsk = current;
-	struct nsproxy *new_nsproxy;
 	struct proc_inode *ei;
 	struct file *file;
 	int err;
@@ -478,18 +494,7 @@ SYSCALL_DEFINE2(setns, unsigned int, nstype, int, fd)
 	    memcmp(&nstype, ops->name.name, ops->name.len)))
 		goto out;
 
-	new_nsproxy = create_new_namespaces(0, tsk, tsk->fs);
-	if (IS_ERR(new_nsproxy)) {
-		err = PTR_ERR(new_nsproxy);
-		goto out;
-	}
-
-	err = ops->install(new_nsproxy, ei->ns);
-	if (err) {
-		free_nsproxy(new_nsproxy);
-		goto out;
-	}
-	switch_task_namespaces(tsk, new_nsproxy);
+	err = kern_setns(ops, ei->ns);
 out:
 	fput(file);
 	return err;
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 10/10] [RFC] Use call_usermodehelper to cleanup after failure
       [not found]       ` <7c6e1ebb39ca920c0c4c572e1f455250b2fbba4b.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2011-03-01  4:05         ` Matt Helsley
  0 siblings, 0 replies; 12+ messages in thread
From: Matt Helsley @ 2011-03-01  4:05 UTC (permalink / raw)
  To: Oren Laadan; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

Without this sys_checkpoint may fail yet still leave files relinked
in <mnt>/lost+found/checkpoint-N/. This patch uses call_usermodehelper()
to clean up those files if sys_checkpoint has failed.

It is possible to do this from userspace instead but due to the
need to parse the image and/or traverse all mount namespaces and mounts
the complexity of the code will be much higher there. That said, this
is an "rm -rf" invoked by the kernel -- not something we want to stuff in
the kernel without considerable forethought. So I've made ths an RFC and,
until this patch gets accepted, we can plan on cleaning up from userspace.

Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
 fs/namei.c |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index f6361f8..0084ceb 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2599,6 +2599,21 @@ static void mkdirname(char *path, char **delim_p)
 	}
 }
 
+static int checkpoint_rm_relink_collection(void *dq_data)
+{
+	char *argv[4], *envp[2];
+
+	argv[0] = "rm";
+	argv[1] = "-rf";
+	argv[2] = dq_data;
+	argv[3] = NULL;
+
+	envp[0] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+	envp[1] = NULL;
+
+	return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
+}
+
 /*
  * Make the directory used to collect all of the links in. Remove it
  * if checkpoint fails.
@@ -2614,6 +2629,9 @@ static int checkpoint_make_relink_collection(struct ckpt_ctx *ctx,
 	if (!delim || (strlen(new_path) < 2)) /* Need a non-empty dirname */
 		return -ENOENT;
 	ret = kernel_sys_mkdir(new_path, S_IRWXU & ~current_umask());
+	if (!ret)
+		deferqueue_add(ctx->err_deferq, new_path, delim - new_path + 1,
+			       checkpoint_rm_relink_collection, NULL);
 	if (delim)
 		*delim = '/';
 	if (ret == -EEXIST) /* already created the collection dir */
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 00/10] Checkpoint/restart of open, unlinked files
       [not found] ` <1298952316-15763-1-git-send-email-matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
  2011-03-01  4:05   ` [PATCH 01/10] Create the .relink file_operation Matt Helsley
@ 2011-03-01  5:05   ` Matt Helsley
  1 sibling, 0 replies; 12+ messages in thread
From: Matt Helsley @ 2011-03-01  5:05 UTC (permalink / raw)
  To: Matt Helsley; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA

Argh, always seem to forget some important details!
This series is based on Oren's ckpt-v23-rc1 tree plus:

1. sys-wrappers
	This patch set from Namhyung Kim wraps "various syscalls
	that were used in init code." These wrappers are also useful
	for the c/r patchset.

2. setns
	Because we're often checkpointing files in an namespace
	other than that occupied by the task calling sys_checkpoint()
	relink fails with EXDEV _unless_ we change to the appropriate
	mount namespace first. This dependency affects patch 9 of the
	series.

Cheers,
	-Matt Helsley

On Mon, Feb 28, 2011 at 08:05:06PM -0800, Matt Helsley wrote:
> This patch set implements the relink file operation and uses it to support
> checkpoint and restart of open, unlinked files. During checkpoint,
> sys_checkpoint relinks the files and returns. Userspace then checkpoints the
> filesystem contents using any backup-like method prior to thawing. That
> backup is then made available for use during an optional migration followed
> by restore and sys_restart. In the case of network and cluster/distributed
> filesystems copying the filesystem contents explicitly for migration may not
> be necessary at all -- it would be part of normal file writes. For
> non-migration uses of checkpoint/restart filesystems like btrfs a snapshot
> could simply be taken during checkpoint and mounted during restart -- again
> without requiring IO proportional to the aggregate size of filesystem
> contents being checkpointed.
> 
> These IO savings are critical to the use of checkpoint/restart as a
> fault mitigation solution in HPC environments where the probability of
> component failure is very high simply due to the number of system
> components. Incurring substantial IO for checkpoint/restart interferes
> with the IO requirements of HPC jobs and thus reduces the frequency of
> checkpoint/restart. That in turn means more processing time is lost
> as a consequence of a fault -- the longer period between checkpoints
> plus the IO required to re-establish hardlinks are simply not acceptable
> for these environments.
> 
> Without relinking we would need to walk the entire filesystem to find out
> that "b" is a path to the same inode (another variation on this case: "b"
> would also have been unlinked). We'd need to do this for every
> unlinked file that remains open in every task to checkpoint. Even then
> there is no guarantee such a "b" exists for every unlinked file -- the
> inodes could be "orphans" -- and we'd need to preserve their contents
> some other way.
> 
> I considered a couple alternatives to preserving unlinked file contents:
> copying and file handles. Each has significant drawbacks.
> 
> First I attempted to copy the file contents into the image and then
> recreate and unlink the file during restart. Using a simple version of
> that method the write above would not reach "b". One fix would be to search
> the filesystem for a file with the same inode number (inode of "b") and
> either open it or hardlink it to "a". Another would be to record the inode
> number. This either shifts the search from checkpoint time to restart time
> or has all the drawbacks of the second method I considered: file handles.
> 
> Instead of copying contents or recording inodes I also considered using
> file handles. We'd need to ensure that the filehandles persist in storage,
> can be snapshotted/backed up, and can be migrated. Can handlefs or any
> generic file handle system do this? My _guess_ is "no" but folks are
> welcome to tell me I'm wrong.
> 
> In contrast, linking the file from a_fd back into its filesystem can avoid
> these complexities. Relinking avoids the search for matching inodes and
> copying large quantities of data from storage only to write it back (in
> fact a non-linking solution requires that the data be read-and-written
> twice -- once for checkpoint and once for restart). Like file handles it does
> require changes to the filesystem code. Unlike file handles, enabling
> relinking does not require every filesystem to support a new kind of
> filesystem "object" -- only an operation that is quite similar to one that
> already exists: link.
> 
> [PATCH 01/10] Create the .relink file_operation
> [PATCH 02/10] ext3/4: Allow relinking to unlinked files
> [PATCH 03/10] Split do_linkat() out of sys_linkat
> [PATCH 04/10] Checkpoint/restart unlinked files
> [PATCH 05/10] Enable c/r of unlinked fifos
> [PATCH 06/10] Support relinking unlinked files in btrfs
> [PATCH 07/10] Add relink_dir superblock field
> [PATCH 08/10] Parse the relink=%s mount option
> [PATCH 09/10] Enabling checkpoint relink of unlinked files inside containers
> [PATCH 10/10] [RFC] Use call_usermodehelper to cleanup after failure
> 
> BUGS:
> 
> 	There's a memory leak (Reported-by: "Jose R. Santos"
> <jrs-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>) that I haven't tracked down completely yet.
> It seems to be in the "relink=" mount option parsing code -- I feel like I
> must be missing some code path related to vfsmount handling.
> 
> Cheers,
> 	-Matt Helsley
> _______________________________________________
> Containers mailing list
> Containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
> https://lists.linux-foundation.org/mailman/listinfo/containers

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2011-03-01  5:05 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-01  4:05 [PATCH 00/10] Checkpoint/restart of open, unlinked files Matt Helsley
     [not found] ` <1298952316-15763-1-git-send-email-matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2011-03-01  4:05   ` [PATCH 01/10] Create the .relink file_operation Matt Helsley
     [not found]     ` <a6df3726c48b6f9e2422e9c12a32c4991bc53a25.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2011-03-01  4:05       ` [PATCH 02/10] ext3/4: Allow relinking to unlinked files Matt Helsley
     [not found]       ` <cbc5f629ca02554497bcb385039a2a300ef897f0.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2011-03-01  4:05         ` [PATCH 03/10] Split do_linkat() out of sys_linkat Matt Helsley
     [not found]       ` <6f547f7b0dd12d409f993dba3fbc899dc52f2a78.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2011-03-01  4:05         ` [PATCH 04/10] Checkpoint/restart unlinked files Matt Helsley
     [not found]       ` <1a7a344ddd1d68ac862ec2248be77f2dd9f138c8.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2011-03-01  4:05         ` [PATCH 05/10] Enable c/r of unlinked fifos Matt Helsley
     [not found]       ` <dbe36642be44152833a63e8173e8f0490dc1573f.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2011-03-01  4:05         ` [PATCH 06/10] Support relinking unlinked files in btrfs Matt Helsley
     [not found]       ` <ac2a9431a97bbb45bdecf2ccd011f70ee99f2731.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2011-03-01  4:05         ` [PATCH 07/10] Add relink_dir superblock field Matt Helsley
     [not found]       ` <24a5e0d9179d6e3c29ec0197a3251963ff2a4dbd.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2011-03-01  4:05         ` [PATCH 08/10] Parse the relink=%s mount option Matt Helsley
     [not found]       ` <3d0a3c702e5986a45676987a4f3a6607d28725e7.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2011-03-01  4:05         ` [PATCH 09/10] Enabling checkpoint relink of unlinked files inside containers Matt Helsley
     [not found]       ` <7c6e1ebb39ca920c0c4c572e1f455250b2fbba4b.1298952288.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2011-03-01  4:05         ` [PATCH 10/10] [RFC] Use call_usermodehelper to cleanup after failure Matt Helsley
2011-03-01  5:05   ` [PATCH 00/10] Checkpoint/restart of open, unlinked files Matt Helsley

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.