From: Mike Snitzer <[email-masked]>

Add 'drop_writes' option to drop writes silently while the
device is 'down'.  Reads are not touched.

Signed-off-by: Mike Snitzer <[email-masked]>
Signed-off-by: Alasdair G Kergon <[email-masked]>

---
drivers/md/dm-flakey.c |   47 +++++++++++++++++++++++++++++++++++++++--------
 Documentation/device-mapper/dm-flakey.txt |   29 ++++++++++---
 drivers/md/dm-flakey.c                    |   63 ++++++++++++++++++++++++++----
 2 files changed, 78 insertions(+), 14 deletions(-)

Index: linux-3.0/Documentation/device-mapper/dm-flakey.txt
===================================================================
--- linux-3.0.orig/Documentation/device-mapper/dm-flakey.txt
+++ linux-3.0/Documentation/device-mapper/dm-flakey.txt
@@ -1,17 +1,34 @@
 dm-flakey
 =========
 
-This target is the same as the linear target except that it returns I/O
-errors periodically.  It's been found useful in simulating failing
-devices for testing purposes.
+This target is the same as the linear target except that it exhibits
+unreliable behaviour periodically.  It's been found useful in simulating
+failing devices for testing purposes.
 
 Starting from the time the table is loaded, the device is available for
-<up interval> seconds, then returns errors for <down interval> seconds,
-and then this cycle repeats.
+<up interval> seconds, then exhibits unreliable behaviour for <down
+interval> seconds, and then this cycle repeats.
 
-Parameters: <dev path> <offset> <up interval> <down interval>
+Also, consider using this in combination with the dm-delay target too,
+which can delay reads and writes and/or send them to different
+underlying devices.
+
+Table parameters
+----------------
+  <dev path> <offset> <up interval> <down interval> \
+    [<num_features> [<feature arguments>]]
+
+Mandatory parameters:
     <dev path>: Full pathname to the underlying block-device, or a
                 "major:minor" device-number.
     <offset>: Starting sector within the device.
     <up interval>: Number of seconds device is available.
     <down interval>: Number of seconds device returns errors.
+
+Optional feature parameters:
+  If no feature parameters are present, during the periods of
+  unreliability, all I/O returns errors.
+
+  drop_writes:
+	All write I/O is silently ignored.
+	Read I/O is handled correctly.
Index: linux-3.0/drivers/md/dm-flakey.c
===================================================================
--- linux-3.0.orig/drivers/md/dm-flakey.c
+++ linux-3.0/drivers/md/dm-flakey.c
@@ -25,16 +25,22 @@ struct flakey_c {
 	sector_t start;
 	unsigned up_interval;
 	unsigned down_interval;
+	unsigned long flags;
 };
 
-static int parse_features(struct dm_arg_set *as, struct dm_target *ti)
+enum feature_flag_bits {
+	DROP_WRITES
+};
+
+static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
+			  struct dm_target *ti)
 {
 	int r;
 	unsigned argc;
 	const char *arg_name;
 
 	static struct dm_arg _args[] = {
-		{0, 0, "Invalid number of feature args"},
+		{0, 1, "Invalid number of feature args"},
 	};
 
 	/* No feature arguments supplied. */
@@ -49,6 +55,18 @@ static int parse_features(struct dm_arg_
 		arg_name = dm_shift_arg(as);
 		argc--;
 
+		/*
+		 * drop_writes
+		 */
+		if (!strcasecmp(arg_name, "drop_writes")) {
+			if (test_and_set_bit(DROP_WRITES, &fc->flags)) {
+				ti->error = "Feature drop_writes duplicated";
+				return -EINVAL;
+			}
+
+			continue;
+		}
+
 		ti->error = "Unrecognised flakey feature requested";
 		r = -EINVAL;
 	}
@@ -59,6 +77,9 @@ static int parse_features(struct dm_arg_
 /*
  * Construct a flakey mapping:
  * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
+ *
+ *   Feature args:
+ *     [drop_writes]
  */
 static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 {
@@ -81,7 +102,7 @@ static int flakey_ctr(struct dm_target *
 		return -EINVAL;
 	}
 
-	fc = kmalloc(sizeof(*fc), GFP_KERNEL);
+	fc = kzalloc(sizeof(*fc), GFP_KERNEL);
 	if (!fc) {
 		ti->error = "Cannot allocate linear context";
 		return -ENOMEM;
@@ -114,7 +135,7 @@ static int flakey_ctr(struct dm_target *
 		goto bad;
 	}
 
-	r = parse_features(&as, ti);
+	r = parse_features(&as, fc, ti);
 	if (r)
 		goto bad;
 
@@ -162,12 +183,31 @@ static int flakey_map(struct dm_target *
 {
 	struct flakey_c *fc = ti->private;
 	unsigned elapsed;
+	unsigned rw;
 
 	/* Are we alive ? */
 	elapsed = (jiffies - fc->start_time) / HZ;
-	if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval)
+	if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
+		rw = bio_data_dir(bio);
+
+		/*
+		 * Drop writes.  Map reads as normal.
+		 */
+		if (test_bit(DROP_WRITES, &fc->flags)) {
+			if (rw == WRITE) {
+				bio_endio(bio, 0);
+				return DM_MAPIO_SUBMITTED;
+			}
+			goto map_bio;
+		}
+
+		/*
+		 * Default setting errors all I/O.
+		 */
 		return -EIO;
+	}
 
+map_bio:
 	flakey_map_bio(ti, bio);
 
 	return DM_MAPIO_REMAPPED;
@@ -176,7 +216,9 @@ static int flakey_map(struct dm_target *
 static int flakey_status(struct dm_target *ti, status_type_t type,
 			 char *result, unsigned int maxlen)
 {
+	unsigned sz = 0;
 	struct flakey_c *fc = ti->private;
+	unsigned drop_writes;
 
 	switch (type) {
 	case STATUSTYPE_INFO:
@@ -184,9 +226,14 @@ static int flakey_status(struct dm_targe
 		break;
 
 	case STATUSTYPE_TABLE:
-		snprintf(result, maxlen, "%s %llu %u %u", fc->dev->name,
-			 (unsigned long long)fc->start, fc->up_interval,
-			 fc->down_interval);
+		DMEMIT("%s %llu %u %u ", fc->dev->name,
+		       (unsigned long long)fc->start, fc->up_interval,
+		       fc->down_interval);
+
+		drop_writes = test_bit(DROP_WRITES, &fc->flags);
+		DMEMIT("%u ", drop_writes);
+		if (drop_writes)
+			DMEMIT("drop_writes ");
 		break;
 	}
 	return 0;