diff mbox

[RFC] block: dm thin: export how block device handles -ENOSPC

Message ID 20150723155032.GA7246@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Mike Snitzer July 23, 2015, 3:50 p.m. UTC
On Thu, Jul 23 2015 at 10:33P -0400,
Mike Snitzer <snitzer@redhat.com> wrote:
 
> If you just want to stub out the call to bdev_get_nospace_strategy() I
> can crank through implementing it once I get a few minutes.

I didn't use a 4th EOPNOTSUPP enum since if a device doesn't have any
special -ENOSPC handling it'd implicitly be FAST_FAILS_IF_NOSPACE.
But if I overlooked some need for it please let me know.

From 1def7c15911bf15dbee96217591856806bd94b80 Mon Sep 17 00:00:00 2001
From: Mike Snitzer <snitzer@redhat.com>
Date: Thu, 23 Jul 2015 11:28:43 -0400
Subject: [PATCH] block: dm thin: export how block device handles -ENOSPC

DM thin provisioning's handling of -ENOSPC from the underlying data
device is configurable.  Export this information so that upper layers
(e.g. XFS) may train their -ENOSPC handling accordingly.

By default all normal block devices won't have any specialized handling
(FAST_FAILS_IF_NOSPACE), if the device queues IO for a specified time it
will fail slowly (SLOW_FAILS_IF_NOSPACE), otherwise it queues IO
indefinitely (NEVER_FAILS_IF_NOSPACE).

Suggested-by: Dave Chinner <david@fromorbit.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
---
 drivers/md/dm-thin.c          | 19 +++++++++++++++++--
 drivers/md/dm.c               | 33 +++++++++++++++++++++++++++++++++
 fs/block_dev.c                | 10 ++++++++++
 include/linux/blkdev.h        |  9 +++++++++
 include/linux/device-mapper.h |  6 ++++++
 5 files changed, 75 insertions(+), 2 deletions(-)
diff mbox

Patch

diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index d2bbe8c..11cbdf1 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -3953,7 +3953,7 @@  static struct target_type pool_target = {
 	.name = "thin-pool",
 	.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
 		    DM_TARGET_IMMUTABLE,
-	.version = {1, 16, 0},
+	.version = {1, 17, 0},
 	.module = THIS_MODULE,
 	.ctr = pool_ctr,
 	.dtr = pool_dtr,
@@ -4338,9 +4338,23 @@  static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
 	limits->max_discard_sectors = 2048 * 1024 * 16; /* 16G */
 }
 
+static enum blk_nospace_strategy thin_get_nospace_strategy(struct dm_target *ti)
+{
+	struct thin_c *tc = ti->private;
+	struct pool *pool = tc->pool;
+
+	if (pool->pf.error_if_no_space)
+		return FAST_FAILS_IF_NOSPACE;
+
+	else if (!ACCESS_ONCE(no_space_timeout_secs))
+		return NEVER_FAILS_IF_NOSPACE;
+
+	return SLOW_FAILS_IF_NOSPACE;
+}
+
 static struct target_type thin_target = {
 	.name = "thin",
-	.version = {1, 16, 0},
+	.version = {1, 17, 0},
 	.module	= THIS_MODULE,
 	.ctr = thin_ctr,
 	.dtr = thin_dtr,
@@ -4353,6 +4367,7 @@  static struct target_type thin_target = {
 	.merge = thin_merge,
 	.iterate_devices = thin_iterate_devices,
 	.io_hints = thin_io_hints,
+	.get_nospace_strategy = thin_get_nospace_strategy,
 };
 
 /*----------------------------------------------------------------*/
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index ab37ae1..226d856 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -597,6 +597,38 @@  out:
 	return r;
 }
 
+static enum blk_nospace_strategy dm_blk_get_nospace_strategy(struct block_device *bdev)
+{
+	struct mapped_device *md = bdev->bd_disk->private_data;
+	int srcu_idx;
+	struct dm_table *map;
+	struct dm_target *tgt;
+	enum blk_nospace_strategy nospace_strategy = FAST_FAILS_IF_NOSPACE;
+
+	map = dm_get_live_table(md, &srcu_idx);
+
+	if (!map || !dm_table_get_size(map))
+		goto out;
+
+	/* We only support devices that have a single target */
+	if (dm_table_get_num_targets(map) != 1)
+		goto out;
+
+	tgt = dm_table_get_target(map, 0);
+	if (!tgt->type->get_nospace_strategy)
+		goto out;
+
+	if (dm_suspended_md(md))
+		goto out;
+
+	nospace_strategy = tgt->type->get_nospace_strategy(tgt);
+
+out:
+	dm_put_live_table(md, srcu_idx);
+
+	return nospace_strategy;
+}
+
 static struct dm_io *alloc_io(struct mapped_device *md)
 {
 	return mempool_alloc(md->io_pool, GFP_NOIO);
@@ -3647,6 +3679,7 @@  static const struct block_device_operations dm_blk_dops = {
 	.release = dm_blk_close,
 	.ioctl = dm_blk_ioctl,
 	.getgeo = dm_blk_getgeo,
+	.get_nospace_strategy = dm_blk_get_nospace_strategy,
 	.owner = THIS_MODULE
 };
 
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 1982437..a492644 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -469,6 +469,16 @@  long bdev_direct_access(struct block_device *bdev, sector_t sector,
 }
 EXPORT_SYMBOL_GPL(bdev_direct_access);
 
+enum blk_nospace_strategy bdev_get_nospace_strategy(struct block_device *bdev)
+{
+	const struct block_device_operations *ops = bdev->bd_disk->fops;
+
+	if (!ops->get_nospace_strategy)
+		return FAST_FAILS_IF_NOSPACE;
+	return ops->get_nospace_strategy(bdev);
+}
+EXPORT_SYMBOL_GPL(bdev_get_nospace_strategy);
+
 /*
  * pseudo-fs
  */
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index d4068c17d..fab5482 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1549,6 +1549,12 @@  static inline bool blk_integrity_is_initialized(struct gendisk *g)
 
 #endif /* CONFIG_BLK_DEV_INTEGRITY */
 
+enum blk_nospace_strategy {
+	FAST_FAILS_IF_NOSPACE,	      /* immediate ENOSPC (no special handling) */
+	SLOW_FAILS_IF_NOSPACE,	      /* queue IO for some time, then ENOSPC */
+	NEVER_FAILS_IF_NOSPACE,	      /* queue IO forever */
+};
+
 struct block_device_operations {
 	int (*open) (struct block_device *, fmode_t);
 	void (*release) (struct gendisk *, fmode_t);
@@ -1566,6 +1572,7 @@  struct block_device_operations {
 	int (*getgeo)(struct block_device *, struct hd_geometry *);
 	/* this callback is with swap_lock and sometimes page table lock held */
 	void (*swap_slot_free_notify) (struct block_device *, unsigned long);
+	enum blk_nospace_strategy (*get_nospace_strategy) (struct block_device *);
 	struct module *owner;
 };
 
@@ -1576,6 +1583,8 @@  extern int bdev_write_page(struct block_device *, sector_t, struct page *,
 						struct writeback_control *);
 extern long bdev_direct_access(struct block_device *, sector_t, void **addr,
 						unsigned long *pfn, long size);
+extern enum blk_nospace_strategy bdev_get_nospace_strategy(struct block_device *);
+
 #else /* CONFIG_BLOCK */
 
 struct block_device;
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 51cc1de..2e3c5d5 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -119,6 +119,11 @@  typedef void (*dm_io_hints_fn) (struct dm_target *ti,
  */
 typedef int (*dm_busy_fn) (struct dm_target *ti);
 
+/*
+ * Returns how the target handles -ENOSPC from lower layers.
+ */
+typedef enum blk_nospace_strategy (*dm_get_nospace_strategy_fn) (struct dm_target *ti);
+
 void dm_error(const char *message);
 
 struct dm_dev {
@@ -164,6 +169,7 @@  struct target_type {
 	dm_busy_fn busy;
 	dm_iterate_devices_fn iterate_devices;
 	dm_io_hints_fn io_hints;
+	dm_get_nospace_strategy_fn get_nospace_strategy;
 
 	/* For internal device-mapper use. */
 	struct list_head list;