[PATCH] lscp: accelerate backward checkpoint listing

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

 



If the minimum checkpoint number of valid checkpoints is large to some
extent, "lscp -r" command takes very long time:

 $ lscp -r
                 CNO        DATE     TIME  MODE  FLG      BLKCNT       ICNT
             6541269  2015-02-11 18:38:30   cp    -          435          2
             6541268  2015-02-11 18:38:25   cp    -          484         51
  <the command looks to enter a busy loop>

This is because it tries to find younger checkpoints tracking back the
checkpoint list in a constant step size.

This fixes the issue by lengthening or shortening the step size
depending on whether the backward search found a younger checkpoint or
not.

This patch also inserts a dummy nilfs_get_cpinfo() call before
starting the backward search to make successive nilfs_get_cpinfo()
calls much faster.

Signed-off-by: Ryusuke Konishi <konishi.ryusuke@xxxxxxxxxxxxx>
---
 bin/lscp.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 87 insertions(+), 9 deletions(-)

diff --git a/bin/lscp.c b/bin/lscp.c
index c855def..023be5c 100644
--- a/bin/lscp.c
+++ b/bin/lscp.c
@@ -84,6 +84,12 @@ static const struct option long_option[] = {
 #define LSCP_NCPINFO	512
 #define LSCP_MINDELTA	64	/* Minimum delta for reverse direction */
 
+enum lscp_state {
+	LSCP_INIT_ST,		/* Initial state */
+	LSCP_NORMAL_ST,		/* Normal state */
+	LSCP_ACCEL_ST,		/* Accelerate state */
+	LSCP_DECEL_ST,		/* Decelerate state */
+};
 
 static __u64 param_index;
 static __u64 param_lines;
@@ -176,35 +182,107 @@ static int lscp_backward_cpinfo(struct nilfs *nilfs,
 	struct nilfs_cpinfo *cpi;
 	nilfs_cno_t sidx; /* start index (inclusive) */
 	nilfs_cno_t eidx; /* end index (exclusive) */
-	__u64 rest, delta;
+	nilfs_cno_t prev_head = 0;
+	__u64 rest, delta, v;
+	int state = LSCP_INIT_ST;
 	ssize_t n;
 
 	rest = param_lines && param_lines < cpstat->cs_ncps ? param_lines :
 		cpstat->cs_ncps;
+	if (!rest)
+		goto out;
 	eidx = param_index && param_index < cpstat->cs_cno ? param_index + 1 :
 		cpstat->cs_cno;
 
-	for ( ; rest > 0 && eidx > NILFS_CNO_MIN; eidx = sidx) {
-		delta = min_t(__u64, LSCP_NCPINFO,
-			      max_t(__u64, rest, LSCP_MINDELTA));
-		sidx = (eidx >= NILFS_CNO_MIN + delta) ? eidx - delta :
-			NILFS_CNO_MIN;
+recalc_delta:
+	delta = min_t(__u64, LSCP_NCPINFO, max_t(__u64, rest, LSCP_MINDELTA));
+	v = delta;
 
-		n = lscp_get_cpinfo(nilfs, sidx, NILFS_CHECKPOINT, eidx - sidx);
+	while (eidx > NILFS_CNO_MIN) {
+		if (eidx < NILFS_CNO_MIN + v || state == LSCP_INIT_ST)
+			sidx = NILFS_CNO_MIN;
+		else
+			sidx = eidx - v;
+
+		n = lscp_get_cpinfo(nilfs, sidx, NILFS_CHECKPOINT,
+				    state == LSCP_NORMAL_ST ? eidx - sidx : 1);
 		if (n < 0)
 			return n;
 		if (!n)
 			break;
 
-		for (cpi = cpinfos + n - 1; cpi >= cpinfos && rest > 0; cpi--) {
+		if (state == LSCP_INIT_ST) {
+			/*
+			 * This state makes succesive
+			 * nilfs_get_cpinfo() calls much faster by
+			 * setting minimum checkpoint number in nilfs
+			 * struct.
+			 */
+			if (cpinfos[0].ci_cno >= eidx)
+				goto out; /* out of range */
+			state = LSCP_NORMAL_ST;
+			continue;
+		} else if (cpinfos[0].ci_cno == prev_head) {
+			/* No younger checkpoint was found */
+
+			if (sidx == NILFS_CNO_MIN)
+				break;
+
+			/* go further back */
+			switch (state) {
+			case LSCP_NORMAL_ST:
+				state = LSCP_ACCEL_ST;
+				/* fall through */
+			case LSCP_ACCEL_ST:
+				if ((v << 1) > v)
+					v <<= 1;
+				break;
+			case LSCP_DECEL_ST:
+				state = LSCP_NORMAL_ST;
+				v = delta;
+				break;
+			}
+			eidx = sidx;
+			continue;
+		} else {
+			switch (state) {
+			case LSCP_ACCEL_ST:
+			case LSCP_DECEL_ST:
+				if (cpinfos[n - 1].ci_cno + 1 < prev_head) {
+					/* search again more slowly */
+					v >>= 1;
+					if (v <= delta) {
+						state = LSCP_NORMAL_ST;
+						v = delta;
+					} else {
+						state = LSCP_DECEL_ST;
+					}
+					continue;
+				}
+				break;
+			default:
+				break;
+			}
+		}
+
+		state = LSCP_NORMAL_ST;
+		cpi = &cpinfos[n - 1];
+		do {
 			if (cpi->ci_cno < eidx &&
 			    (show_all || nilfs_cpinfo_snapshot(cpi) ||
 			     !nilfs_cpinfo_minor(cpi))) {
 				lscp_print_cpinfo(cpi);
 				rest--;
+				if (rest == 0)
+					goto out;
 			}
-		}
+		} while (--cpi >= cpinfos);
+
+		prev_head = cpinfos[0].ci_cno;
+		eidx = sidx;
+		goto recalc_delta;
 	}
+out:
 	return 0;
 }
 
-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-nilfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Filesystem Development]     [Linux BTRFS]     [Linux CIFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux