@@ -202,6 +202,16 @@ cvt_daddr_to_agno(
return cvt_bb_to_off_fsbt(xfd, daddr) / xfd->fsgeom.agblocks;
}
+/* Convert sparse filesystem block to AG Number */
+static inline uint32_t
+cvt_fsb_to_agno(
+ struct xfs_fd *xfd,
+ uint64_t fsbno)
+{
+ return fsbno >> xfd->agblklog;
+}
+
+
/* Convert sector number to AG block number. */
static inline uint32_t
cvt_daddr_to_agbno(
@@ -202,9 +202,17 @@ Wait for removal to complete.
.TP
.B print
Display a list of all open files.
+.TP
+.BI "relocate \-a agno [ \-h agno ]"
+Empty out the given allocation group by moving file data elsewhere.
+The
+.B -h
+option specifies the highest allocation group into which we can move data.
+
.TP
.B resolve_owner
Resolves space in the filesystem to file paths, maybe?
+
.TP
.B quit
Exit
@@ -9,10 +9,10 @@
#include <linux/fiemap.h>
#include "libfrog/fsgeom.h"
#include "libfrog/radix-tree.h"
-#include "command.h"
-#include "init.h"
#include "libfrog/paths.h"
#include <linux/fsmap.h>
+#include "command.h"
+#include "init.h"
#include "space.h"
#include "input.h"
#include "relocation.h"
@@ -65,8 +65,8 @@ track_inode(
set_reloc_iflag(owner, MOVE_BLOCKS);
}
-static void
-scan_ag(
+int
+find_relocation_targets(
xfs_agnumber_t agno)
{
struct fsmap_head *fsmap;
@@ -80,8 +80,7 @@ scan_ag(
fsmap = malloc(fsmap_sizeof(NR_EXTENTS));
if (!fsmap) {
fprintf(stderr, _("%s: fsmap malloc failed.\n"), progname);
- exitcode = 1;
- return;
+ return -ENOMEM;
}
memset(fsmap, 0, sizeof(*fsmap));
@@ -102,8 +101,7 @@ scan_ag(
fprintf(stderr, _("%s: FS_IOC_GETFSMAP [\"%s\"]: %s\n"),
progname, file->name, strerror(errno));
free(fsmap);
- exitcode = 1;
- return;
+ return -errno;
}
/* No more extents to map, exit */
@@ -148,6 +146,7 @@ scan_ag(
}
free(fsmap);
+ return 0;
}
/*
@@ -159,6 +158,7 @@ find_owner_f(
char **argv)
{
xfs_agnumber_t agno = -1;
+ int ret;
int c;
while ((c = getopt(argc, argv, "a:")) != EOF) {
@@ -198,7 +198,9 @@ _("Filesystem at %s does not have reverse mapping enabled. Aborting.\n"),
return 0;
}
- scan_ag(agno);
+ ret = find_relocation_targets(agno);
+ if (ret)
+ exitcode = 1;
return 0;
}
@@ -299,8 +301,8 @@ _("Aborting: Storing path %s for inode 0x%lx failed: %s\n"),
* This should be parallelised - pass subdirs off to a work queue, have the
* work queue processes subdirs, queueing more subdirs to work on.
*/
-static int
-walk_mount(
+int
+resolve_target_paths(
const char *mntpt)
{
int ret;
@@ -361,9 +363,9 @@ list_inode_paths(void)
/*
* Any inodes remaining in the tree at this point indicate inodes whose
- * paths were not found. This will be unlinked but still open inodes or
- * lost inodes due to corruptions. Either way, a shrink will not succeed
- * until these inodes are removed from the filesystem.
+ * paths were not found. This will be free inodes or unlinked but still
+ * open inodes. Either way, a shrink will not succeed until these inodes
+ * are removed from the filesystem.
*/
idx = 0;
do {
@@ -400,7 +402,7 @@ _("Inode list has not been populated. No inodes to resolve.\n"));
return 0;
}
- ret = walk_mount(file->fs_path.fs_dir);
+ ret = resolve_target_paths(file->fs_path.fs_dir);
if (ret) {
fprintf(stderr,
_("Failed to resolve all paths from mount point %s: %s\n"),
@@ -40,6 +40,7 @@ init_commands(void)
move_inode_init();
find_owner_init();
resolve_owner_init();
+ relocate_init();
}
static int
@@ -12,6 +12,7 @@
#include "space.h"
#include "input.h"
#include "handle.h"
+#include "relocation.h"
#include <linux/fiemap.h>
#include <linux/falloc.h>
@@ -404,8 +405,8 @@ exchange_inodes(
return 0;
}
-static int
-move_file_to_ag(
+int
+relocate_file_to_ag(
const char *mnt,
const char *path,
struct xfs_fd *xfd,
@@ -511,7 +512,7 @@ _("Destination AG %d does not exist. Filesystem only has %d AGs\n"),
}
if (S_ISREG(st.st_mode)) {
- ret = move_file_to_ag(file->fs_path.fs_dir, file->name,
+ ret = relocate_file_to_ag(file->fs_path.fs_dir, file->name,
&file->xfd, agno);
} else {
fprintf(stderr, _("Unsupported: %s is not a regular file.\n"),
@@ -315,3 +315,237 @@ forget_reloc_ino(
free(rln);
}
#endif /* USE_RADIX_TREE_FOR_INUMS */
+
+static struct cmdinfo relocate_cmd;
+
+static int
+relocate_targets_to_ag(
+ const char *mnt,
+ xfs_agnumber_t dst_agno)
+{
+ struct inode_path *ipath;
+ uint64_t idx = 0;
+ int ret = 0;
+
+ do {
+ struct xfs_fd xfd = {0};
+ struct stat st;
+
+ /* lookup first relocation target */
+ ipath = get_next_reloc_ipath(idx);
+ if (!ipath)
+ break;
+
+ /* XXX: don't handle hard link cases yet */
+ if (ipath->link_count > 1) {
+ fprintf(stderr,
+ "FIXME! Skipping hardlinked inode at path %s\n",
+ ipath->path);
+ goto next;
+ }
+
+
+ ret = stat(ipath->path, &st);
+ if (ret) {
+ fprintf(stderr, _("stat(%s) failed: %s\n"),
+ ipath->path, strerror(errno));
+ goto next;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ fprintf(stderr,
+ _("FIXME! Skipping %s: not a regular file.\n"),
+ ipath->path);
+ goto next;
+ }
+
+ ret = xfd_open(&xfd, ipath->path, O_RDONLY);
+ if (ret) {
+ fprintf(stderr, _("xfd_open(%s) failed: %s\n"),
+ ipath->path, strerror(-ret));
+ goto next;
+ }
+
+ /* move to destination AG */
+ ret = relocate_file_to_ag(mnt, ipath->path, &xfd, dst_agno);
+ xfd_close(&xfd);
+
+ /*
+ * If the destination AG has run out of space, we do not remove
+ * this inode from relocation data so it will be immediately
+ * retried in the next AG. Other errors will be fatal.
+ */
+ if (ret < 0)
+ return ret;
+next:
+ /* remove from relocation data */
+ idx = ipath->ino + 1;
+ forget_reloc_ino(ipath->ino);
+ } while (ret != -ENOSPC);
+
+ return ret;
+}
+
+static int
+relocate_targets(
+ const char *mnt,
+ xfs_agnumber_t highest_agno)
+{
+ xfs_agnumber_t dst_agno = 0;
+ int ret;
+
+ for (dst_agno = 0; dst_agno <= highest_agno; dst_agno++) {
+ ret = relocate_targets_to_ag(mnt, dst_agno);
+ if (ret == -ENOSPC)
+ continue;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Relocate all the user objects in an AG to lower numbered AGs.
+ */
+static int
+relocate_f(
+ int argc,
+ char **argv)
+{
+ xfs_agnumber_t target_agno = -1;
+ xfs_agnumber_t highest_agno = -1;
+ xfs_agnumber_t log_agno;
+ void *fshandle;
+ size_t fshdlen;
+ int c;
+ int ret;
+
+ while ((c = getopt(argc, argv, "a:h:")) != EOF) {
+ switch (c) {
+ case 'a':
+ target_agno = cvt_u32(optarg, 10);
+ if (errno) {
+ fprintf(stderr, _("bad target agno value %s\n"),
+ optarg);
+ return command_usage(&relocate_cmd);
+ }
+ break;
+ case 'h':
+ highest_agno = cvt_u32(optarg, 10);
+ if (errno) {
+ fprintf(stderr, _("bad highest agno value %s\n"),
+ optarg);
+ return command_usage(&relocate_cmd);
+ }
+ break;
+ default:
+ return command_usage(&relocate_cmd);
+ }
+ }
+
+ if (optind != argc)
+ return command_usage(&relocate_cmd);
+
+ if (target_agno == -1) {
+ fprintf(stderr, _("Target AG must be specified!\n"));
+ return command_usage(&relocate_cmd);
+ }
+
+ log_agno = cvt_fsb_to_agno(&file->xfd, file->xfd.fsgeom.logstart);
+ if (target_agno <= log_agno) {
+ fprintf(stderr,
+_("Target AG %d must be higher than the journal AG (AG %d). Aborting.\n"),
+ target_agno, log_agno);
+ goto out_fail;
+ }
+
+ if (target_agno >= file->xfd.fsgeom.agcount) {
+ fprintf(stderr,
+_("Target AG %d does not exist. Filesystem only has %d AGs\n"),
+ target_agno, file->xfd.fsgeom.agcount);
+ goto out_fail;
+ }
+
+ if (highest_agno == -1)
+ highest_agno = target_agno - 1;
+
+ if (highest_agno >= target_agno) {
+ fprintf(stderr,
+_("Highest destination AG %d must be less than target AG %d. Aborting.\n"),
+ highest_agno, target_agno);
+ goto out_fail;
+ }
+
+ if (is_reloc_populated()) {
+ fprintf(stderr,
+_("Relocation data populated from previous commands. Aborting.\n"));
+ goto out_fail;
+ }
+
+ /* this is so we can use fd_to_handle() later on */
+ ret = path_to_fshandle(file->fs_path.fs_dir, &fshandle, &fshdlen);
+ if (ret < 0) {
+ fprintf(stderr, _("Cannot get fshandle for mount %s: %s\n"),
+ file->fs_path.fs_dir, strerror(errno));
+ goto out_fail;
+ }
+
+ ret = find_relocation_targets(target_agno);
+ if (ret) {
+ fprintf(stderr,
+_("Failure during target discovery. Aborting.\n"));
+ goto out_fail;
+ }
+
+ ret = resolve_target_paths(file->fs_path.fs_dir);
+ if (ret) {
+ fprintf(stderr,
+_("Failed to resolve all paths from mount point %s: %s\n"),
+ file->fs_path.fs_dir, strerror(-ret));
+ goto out_fail;
+ }
+
+ ret = relocate_targets(file->fs_path.fs_dir, highest_agno);
+ if (ret) {
+ fprintf(stderr,
+_("Failed to relocate all targets out of AG %d: %s\n"),
+ target_agno, strerror(-ret));
+ goto out_fail;
+ }
+
+ return 0;
+out_fail:
+ exitcode = 1;
+ return 0;
+}
+
+static void
+relocate_help(void)
+{
+ printf(_(
+"\n"
+"Relocate all the user data and metadata in an AG.\n"
+"\n"
+"This function will discover all the relocatable objects in a single AG and\n"
+"move them to a lower AG as preparation for a shrink operation.\n"
+"\n"
+" -a <agno> Allocation group to empty\n"
+" -h <agno> Highest target AG allowed to relocate into\n"
+"\n"));
+
+}
+
+void
+relocate_init(void)
+{
+ relocate_cmd.name = "relocate";
+ relocate_cmd.altname = "relocate";
+ relocate_cmd.cfunc = relocate_f;
+ relocate_cmd.argmin = 2;
+ relocate_cmd.argmax = 4;
+ relocate_cmd.args = "-a agno [-h agno]";
+ relocate_cmd.flags = CMD_FLAG_ONESHOT;
+ relocate_cmd.oneline = _("Relocate data in an AG.");
+ relocate_cmd.help = relocate_help;
+
+ add_command(&relocate_cmd);
+}
@@ -43,4 +43,9 @@ struct inode_path {
*/
#define UNLINKED_IPATH ((struct inode_path *)1)
+int find_relocation_targets(xfs_agnumber_t agno);
+int relocate_file_to_ag(const char *mnt, const char *path, struct xfs_fd *xfd,
+ xfs_agnumber_t agno);
+int resolve_target_paths(const char *mntpt);
+
#endif /* XFS_SPACEMAN_RELOCATION_H_ */
@@ -41,5 +41,6 @@ extern void health_init(void);
void move_inode_init(void);
void find_owner_init(void);
void resolve_owner_init(void);
+void relocate_init(void);
#endif /* XFS_SPACEMAN_SPACE_H_ */