Commit 04445556 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'dm-4.3-fixes-2' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm

Pull dm fixes from Mike Snitzer:
 "Three stable fixes:

   - DM core AB-BA deadlock fix in the device destruction path (vs
     device creation's DM table swap).

   - DM raid fix to properly round up the region_size to the next
     power-of-2.

   - DM cache fix for a NULL pointer seen while switching from the
     "cleaner" cache policy.

  Two fixes for regressions introduced during the 4.3 merge:

   - request-based DM error propagation regressed due to incorrect
     changes introduced when adding the bi_error field to bio.

   - DM snapshot fix to only support snapshots that overflow if the
     client (e.g. lvm2) is prepared to deal with the associated
     snapshot status interface change"

* tag 'dm-4.3-fixes-2' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm snapshot: add new persistent store option to support overflow
  dm cache: fix NULL pointer when switching from cleaner policy
  dm: fix request-based dm error reporting
  dm raid: fix round up of default region size
  dm: fix AB-BA deadlock in __dm_destroy()
parents 175d58cf b0d3cc01
...@@ -41,9 +41,13 @@ useless and be disabled, returning errors. So it is important to monitor ...@@ -41,9 +41,13 @@ useless and be disabled, returning errors. So it is important to monitor
the amount of free space and expand the <COW device> before it fills up. the amount of free space and expand the <COW device> before it fills up.
<persistent?> is P (Persistent) or N (Not persistent - will not survive <persistent?> is P (Persistent) or N (Not persistent - will not survive
after reboot). after reboot). O (Overflow) can be added as a persistent store option
The difference is that for transient snapshots less metadata must be to allow userspace to advertise its support for seeing "Overflow" in the
saved on disk - they can be kept in memory by the kernel. snapshot status. So supported store types are "P", "PO" and "N".
The difference between persistent and transient is with transient
snapshots less metadata must be saved on disk - they can be kept in
memory by the kernel.
* snapshot-merge <origin> <COW device> <persistent> <chunksize> * snapshot-merge <origin> <COW device> <persistent> <chunksize>
......
...@@ -436,7 +436,7 @@ static struct dm_cache_policy *wb_create(dm_cblock_t cache_size, ...@@ -436,7 +436,7 @@ static struct dm_cache_policy *wb_create(dm_cblock_t cache_size,
static struct dm_cache_policy_type wb_policy_type = { static struct dm_cache_policy_type wb_policy_type = {
.name = "cleaner", .name = "cleaner",
.version = {1, 0, 0}, .version = {1, 0, 0},
.hint_size = 0, .hint_size = 4,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.create = wb_create .create = wb_create
}; };
......
...@@ -203,7 +203,7 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv, ...@@ -203,7 +203,7 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
return -EINVAL; return -EINVAL;
} }
tmp_store = kmalloc(sizeof(*tmp_store), GFP_KERNEL); tmp_store = kzalloc(sizeof(*tmp_store), GFP_KERNEL);
if (!tmp_store) { if (!tmp_store) {
ti->error = "Exception store allocation failed"; ti->error = "Exception store allocation failed";
return -ENOMEM; return -ENOMEM;
...@@ -215,7 +215,7 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv, ...@@ -215,7 +215,7 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
else if (persistent == 'N') else if (persistent == 'N')
type = get_type("N"); type = get_type("N");
else { else {
ti->error = "Persistent flag is not P or N"; ti->error = "Exception store type is not P or N";
r = -EINVAL; r = -EINVAL;
goto bad_type; goto bad_type;
} }
...@@ -233,7 +233,7 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv, ...@@ -233,7 +233,7 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
if (r) if (r)
goto bad; goto bad;
r = type->ctr(tmp_store, 0, NULL); r = type->ctr(tmp_store, (strlen(argv[0]) > 1 ? &argv[0][1] : NULL));
if (r) { if (r) {
ti->error = "Exception store type constructor failed"; ti->error = "Exception store type constructor failed";
goto bad; goto bad;
......
...@@ -42,8 +42,7 @@ struct dm_exception_store_type { ...@@ -42,8 +42,7 @@ struct dm_exception_store_type {
const char *name; const char *name;
struct module *module; struct module *module;
int (*ctr) (struct dm_exception_store *store, int (*ctr) (struct dm_exception_store *store, char *options);
unsigned argc, char **argv);
/* /*
* Destroys this object when you've finished with it. * Destroys this object when you've finished with it.
...@@ -123,6 +122,8 @@ struct dm_exception_store { ...@@ -123,6 +122,8 @@ struct dm_exception_store {
unsigned chunk_shift; unsigned chunk_shift;
void *context; void *context;
bool userspace_supports_overflow;
}; };
/* /*
......
...@@ -329,8 +329,7 @@ static int validate_region_size(struct raid_set *rs, unsigned long region_size) ...@@ -329,8 +329,7 @@ static int validate_region_size(struct raid_set *rs, unsigned long region_size)
*/ */
if (min_region_size > (1 << 13)) { if (min_region_size > (1 << 13)) {
/* If not a power of 2, make it the next power of 2 */ /* If not a power of 2, make it the next power of 2 */
if (min_region_size & (min_region_size - 1)) region_size = roundup_pow_of_two(min_region_size);
region_size = 1 << fls(region_size);
DMINFO("Choosing default region size of %lu sectors", DMINFO("Choosing default region size of %lu sectors",
region_size); region_size);
} else { } else {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "dm-exception-store.h" #include "dm-exception-store.h"
#include <linux/ctype.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
...@@ -843,8 +844,7 @@ static void persistent_drop_snapshot(struct dm_exception_store *store) ...@@ -843,8 +844,7 @@ static void persistent_drop_snapshot(struct dm_exception_store *store)
DMWARN("write header failed"); DMWARN("write header failed");
} }
static int persistent_ctr(struct dm_exception_store *store, static int persistent_ctr(struct dm_exception_store *store, char *options)
unsigned argc, char **argv)
{ {
struct pstore *ps; struct pstore *ps;
...@@ -873,6 +873,16 @@ static int persistent_ctr(struct dm_exception_store *store, ...@@ -873,6 +873,16 @@ static int persistent_ctr(struct dm_exception_store *store,
return -ENOMEM; return -ENOMEM;
} }
if (options) {
char overflow = toupper(options[0]);
if (overflow == 'O')
store->userspace_supports_overflow = true;
else {
DMERR("Unsupported persistent store option: %s", options);
return -EINVAL;
}
}
store->context = ps; store->context = ps;
return 0; return 0;
...@@ -888,7 +898,8 @@ static unsigned persistent_status(struct dm_exception_store *store, ...@@ -888,7 +898,8 @@ static unsigned persistent_status(struct dm_exception_store *store,
case STATUSTYPE_INFO: case STATUSTYPE_INFO:
break; break;
case STATUSTYPE_TABLE: case STATUSTYPE_TABLE:
DMEMIT(" P %llu", (unsigned long long)store->chunk_size); DMEMIT(" %s %llu", store->userspace_supports_overflow ? "PO" : "P",
(unsigned long long)store->chunk_size);
} }
return sz; return sz;
......
...@@ -70,8 +70,7 @@ static void transient_usage(struct dm_exception_store *store, ...@@ -70,8 +70,7 @@ static void transient_usage(struct dm_exception_store *store,
*metadata_sectors = 0; *metadata_sectors = 0;
} }
static int transient_ctr(struct dm_exception_store *store, static int transient_ctr(struct dm_exception_store *store, char *options)
unsigned argc, char **argv)
{ {
struct transient_c *tc; struct transient_c *tc;
......
...@@ -1098,7 +1098,7 @@ static void stop_merge(struct dm_snapshot *s) ...@@ -1098,7 +1098,7 @@ static void stop_merge(struct dm_snapshot *s)
} }
/* /*
* Construct a snapshot mapping: <origin_dev> <COW-dev> <p/n> <chunk-size> * Construct a snapshot mapping: <origin_dev> <COW-dev> <p|po|n> <chunk-size>
*/ */
static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{ {
...@@ -1302,6 +1302,7 @@ static void __handover_exceptions(struct dm_snapshot *snap_src, ...@@ -1302,6 +1302,7 @@ static void __handover_exceptions(struct dm_snapshot *snap_src,
u.store_swap = snap_dest->store; u.store_swap = snap_dest->store;
snap_dest->store = snap_src->store; snap_dest->store = snap_src->store;
snap_dest->store->userspace_supports_overflow = u.store_swap->userspace_supports_overflow;
snap_src->store = u.store_swap; snap_src->store = u.store_swap;
snap_dest->store->snap = snap_dest; snap_dest->store->snap = snap_dest;
...@@ -1739,8 +1740,11 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio) ...@@ -1739,8 +1740,11 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
pe = __find_pending_exception(s, pe, chunk); pe = __find_pending_exception(s, pe, chunk);
if (!pe) { if (!pe) {
if (s->store->userspace_supports_overflow) {
s->snapshot_overflowed = 1; s->snapshot_overflowed = 1;
DMERR("Snapshot overflowed: Unable to allocate exception."); DMERR("Snapshot overflowed: Unable to allocate exception.");
} else
__invalidate_snapshot(s, -ENOMEM);
r = -EIO; r = -EIO;
goto out_unlock; goto out_unlock;
} }
...@@ -2365,7 +2369,7 @@ static struct target_type origin_target = { ...@@ -2365,7 +2369,7 @@ static struct target_type origin_target = {
static struct target_type snapshot_target = { static struct target_type snapshot_target = {
.name = "snapshot", .name = "snapshot",
.version = {1, 14, 0}, .version = {1, 15, 0},
.module = THIS_MODULE, .module = THIS_MODULE,
.ctr = snapshot_ctr, .ctr = snapshot_ctr,
.dtr = snapshot_dtr, .dtr = snapshot_dtr,
...@@ -2379,7 +2383,7 @@ static struct target_type snapshot_target = { ...@@ -2379,7 +2383,7 @@ static struct target_type snapshot_target = {
static struct target_type merge_target = { static struct target_type merge_target = {
.name = dm_snapshot_merge_target_name, .name = dm_snapshot_merge_target_name,
.version = {1, 3, 0}, .version = {1, 4, 0},
.module = THIS_MODULE, .module = THIS_MODULE,
.ctr = snapshot_ctr, .ctr = snapshot_ctr,
.dtr = snapshot_dtr, .dtr = snapshot_dtr,
......
...@@ -1001,6 +1001,7 @@ static void end_clone_bio(struct bio *clone) ...@@ -1001,6 +1001,7 @@ static void end_clone_bio(struct bio *clone)
struct dm_rq_target_io *tio = info->tio; struct dm_rq_target_io *tio = info->tio;
struct bio *bio = info->orig; struct bio *bio = info->orig;
unsigned int nr_bytes = info->orig->bi_iter.bi_size; unsigned int nr_bytes = info->orig->bi_iter.bi_size;
int error = clone->bi_error;
bio_put(clone); bio_put(clone);
...@@ -1011,13 +1012,13 @@ static void end_clone_bio(struct bio *clone) ...@@ -1011,13 +1012,13 @@ static void end_clone_bio(struct bio *clone)
* the remainder. * the remainder.
*/ */
return; return;
else if (bio->bi_error) { else if (error) {
/* /*
* Don't notice the error to the upper layer yet. * Don't notice the error to the upper layer yet.
* The error handling decision is made by the target driver, * The error handling decision is made by the target driver,
* when the request is completed. * when the request is completed.
*/ */
tio->error = bio->bi_error; tio->error = error;
return; return;
} }
...@@ -2837,8 +2838,6 @@ static void __dm_destroy(struct mapped_device *md, bool wait) ...@@ -2837,8 +2838,6 @@ static void __dm_destroy(struct mapped_device *md, bool wait)
might_sleep(); might_sleep();
map = dm_get_live_table(md, &srcu_idx);
spin_lock(&_minor_lock); spin_lock(&_minor_lock);
idr_replace(&_minor_idr, MINOR_ALLOCED, MINOR(disk_devt(dm_disk(md)))); idr_replace(&_minor_idr, MINOR_ALLOCED, MINOR(disk_devt(dm_disk(md))));
set_bit(DMF_FREEING, &md->flags); set_bit(DMF_FREEING, &md->flags);
...@@ -2852,14 +2851,14 @@ static void __dm_destroy(struct mapped_device *md, bool wait) ...@@ -2852,14 +2851,14 @@ static void __dm_destroy(struct mapped_device *md, bool wait)
* do not race with internal suspend. * do not race with internal suspend.
*/ */
mutex_lock(&md->suspend_lock); mutex_lock(&md->suspend_lock);
map = dm_get_live_table(md, &srcu_idx);
if (!dm_suspended_md(md)) { if (!dm_suspended_md(md)) {
dm_table_presuspend_targets(map); dm_table_presuspend_targets(map);
dm_table_postsuspend_targets(map); dm_table_postsuspend_targets(map);
} }
mutex_unlock(&md->suspend_lock);
/* dm_put_live_table must be before msleep, otherwise deadlock is possible */ /* dm_put_live_table must be before msleep, otherwise deadlock is possible */
dm_put_live_table(md, srcu_idx); dm_put_live_table(md, srcu_idx);
mutex_unlock(&md->suspend_lock);
/* /*
* Rare, but there may be I/O requests still going to complete, * Rare, but there may be I/O requests still going to complete,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment