Skip to content

Commit b735fed

Browse files
Mikulas Patockasnitm
authored andcommitted
dm snapshot: suspend origin when doing exception handover
In the function snapshot_resume we perform exception store handover. If there is another active snapshot target, the exception store is moved from this target to the target that is being resumed. The problem is that if there is some pending exception, it will point to an incorrect exception store after that handover, causing a crash due to dm-snap-persistent.c:get_exception()'s BUG_ON. This bug can be triggered by repeatedly changing snapshot permissions with "lvchange -p r" and "lvchange -p rw" while there are writes on the associated origin device. To fix this bug, we must suspend the origin device when doing the exception store handover to make sure that there are no pending exceptions: - introduce _origin_hash that keeps track of dm_origin structures. - introduce functions __lookup_dm_origin, __insert_dm_origin and __remove_dm_origin that manipulate the origin hash. - modify snapshot_resume so that it calls dm_internal_suspend_fast() and dm_internal_resume_fast() on the origin device. NOTE to stable@ people: When backporting to kernels 3.12-3.18, use dm_internal_suspend and dm_internal_resume instead of dm_internal_suspend_fast and dm_internal_resume_fast. When backporting to kernels older than 3.12, you need to pick functions dm_internal_suspend and dm_internal_resume from the commit fd2ed4d. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com> Cc: stable@vger.kernel.org
1 parent ab7c7bb commit b735fed

File tree

2 files changed

+86
-9
lines changed

2 files changed

+86
-9
lines changed

drivers/md/dm-snap.c

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <linux/log2.h>
2121
#include <linux/dm-kcopyd.h>
2222

23+
#include "dm.h"
24+
2325
#include "dm-exception-store.h"
2426

2527
#define DM_MSG_PREFIX "snapshots"
@@ -290,13 +292,24 @@ struct origin {
290292
struct list_head snapshots;
291293
};
292294

295+
/*
296+
* This structure is allocated for each origin target
297+
*/
298+
struct dm_origin {
299+
struct dm_dev *dev;
300+
struct dm_target *ti;
301+
unsigned split_boundary;
302+
struct list_head hash_list;
303+
};
304+
293305
/*
294306
* Size of the hash table for origin volumes. If we make this
295307
* the size of the minors list then it should be nearly perfect
296308
*/
297309
#define ORIGIN_HASH_SIZE 256
298310
#define ORIGIN_MASK 0xFF
299311
static struct list_head *_origins;
312+
static struct list_head *_dm_origins;
300313
static struct rw_semaphore _origins_lock;
301314

302315
static DECLARE_WAIT_QUEUE_HEAD(_pending_exceptions_done);
@@ -310,12 +323,22 @@ static int init_origin_hash(void)
310323
_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
311324
GFP_KERNEL);
312325
if (!_origins) {
313-
DMERR("unable to allocate memory");
326+
DMERR("unable to allocate memory for _origins");
314327
return -ENOMEM;
315328
}
316-
317329
for (i = 0; i < ORIGIN_HASH_SIZE; i++)
318330
INIT_LIST_HEAD(_origins + i);
331+
332+
_dm_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
333+
GFP_KERNEL);
334+
if (!_dm_origins) {
335+
DMERR("unable to allocate memory for _dm_origins");
336+
kfree(_origins);
337+
return -ENOMEM;
338+
}
339+
for (i = 0; i < ORIGIN_HASH_SIZE; i++)
340+
INIT_LIST_HEAD(_dm_origins + i);
341+
319342
init_rwsem(&_origins_lock);
320343

321344
return 0;
@@ -324,6 +347,7 @@ static int init_origin_hash(void)
324347
static void exit_origin_hash(void)
325348
{
326349
kfree(_origins);
350+
kfree(_dm_origins);
327351
}
328352

329353
static unsigned origin_hash(struct block_device *bdev)
@@ -350,6 +374,30 @@ static void __insert_origin(struct origin *o)
350374
list_add_tail(&o->hash_list, sl);
351375
}
352376

377+
static struct dm_origin *__lookup_dm_origin(struct block_device *origin)
378+
{
379+
struct list_head *ol;
380+
struct dm_origin *o;
381+
382+
ol = &_dm_origins[origin_hash(origin)];
383+
list_for_each_entry (o, ol, hash_list)
384+
if (bdev_equal(o->dev->bdev, origin))
385+
return o;
386+
387+
return NULL;
388+
}
389+
390+
static void __insert_dm_origin(struct dm_origin *o)
391+
{
392+
struct list_head *sl = &_dm_origins[origin_hash(o->dev->bdev)];
393+
list_add_tail(&o->hash_list, sl);
394+
}
395+
396+
static void __remove_dm_origin(struct dm_origin *o)
397+
{
398+
list_del(&o->hash_list);
399+
}
400+
353401
/*
354402
* _origins_lock must be held when calling this function.
355403
* Returns number of snapshots registered using the supplied cow device, plus:
@@ -1841,8 +1889,20 @@ static void snapshot_resume(struct dm_target *ti)
18411889
{
18421890
struct dm_snapshot *s = ti->private;
18431891
struct dm_snapshot *snap_src = NULL, *snap_dest = NULL;
1892+
struct dm_origin *o;
1893+
struct mapped_device *origin_md = NULL;
18441894

18451895
down_read(&_origins_lock);
1896+
1897+
o = __lookup_dm_origin(s->origin->bdev);
1898+
if (o)
1899+
origin_md = dm_table_get_md(o->ti->table);
1900+
if (origin_md == dm_table_get_md(ti->table))
1901+
origin_md = NULL;
1902+
1903+
if (origin_md)
1904+
dm_internal_suspend_fast(origin_md);
1905+
18461906
(void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL);
18471907
if (snap_src && snap_dest) {
18481908
down_write(&snap_src->lock);
@@ -1851,6 +1911,10 @@ static void snapshot_resume(struct dm_target *ti)
18511911
up_write(&snap_dest->lock);
18521912
up_write(&snap_src->lock);
18531913
}
1914+
1915+
if (origin_md)
1916+
dm_internal_resume_fast(origin_md);
1917+
18541918
up_read(&_origins_lock);
18551919

18561920
/* Now we have correct chunk size, reregister */
@@ -2133,11 +2197,6 @@ static int origin_write_extent(struct dm_snapshot *merging_snap,
21332197
* Origin: maps a linear range of a device, with hooks for snapshotting.
21342198
*/
21352199

2136-
struct dm_origin {
2137-
struct dm_dev *dev;
2138-
unsigned split_boundary;
2139-
};
2140-
21412200
/*
21422201
* Construct an origin mapping: <dev_path>
21432202
* The context for an origin is merely a 'struct dm_dev *'
@@ -2166,6 +2225,7 @@ static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
21662225
goto bad_open;
21672226
}
21682227

2228+
o->ti = ti;
21692229
ti->private = o;
21702230
ti->num_flush_bios = 1;
21712231

@@ -2180,6 +2240,7 @@ static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
21802240
static void origin_dtr(struct dm_target *ti)
21812241
{
21822242
struct dm_origin *o = ti->private;
2243+
21832244
dm_put_device(ti, o->dev);
21842245
kfree(o);
21852246
}
@@ -2216,6 +2277,19 @@ static void origin_resume(struct dm_target *ti)
22162277
struct dm_origin *o = ti->private;
22172278

22182279
o->split_boundary = get_origin_minimum_chunksize(o->dev->bdev);
2280+
2281+
down_write(&_origins_lock);
2282+
__insert_dm_origin(o);
2283+
up_write(&_origins_lock);
2284+
}
2285+
2286+
static void origin_postsuspend(struct dm_target *ti)
2287+
{
2288+
struct dm_origin *o = ti->private;
2289+
2290+
down_write(&_origins_lock);
2291+
__remove_dm_origin(o);
2292+
up_write(&_origins_lock);
22192293
}
22202294

22212295
static void origin_status(struct dm_target *ti, status_type_t type,
@@ -2258,20 +2332,21 @@ static int origin_iterate_devices(struct dm_target *ti,
22582332

22592333
static struct target_type origin_target = {
22602334
.name = "snapshot-origin",
2261-
.version = {1, 8, 1},
2335+
.version = {1, 9, 0},
22622336
.module = THIS_MODULE,
22632337
.ctr = origin_ctr,
22642338
.dtr = origin_dtr,
22652339
.map = origin_map,
22662340
.resume = origin_resume,
2341+
.postsuspend = origin_postsuspend,
22672342
.status = origin_status,
22682343
.merge = origin_merge,
22692344
.iterate_devices = origin_iterate_devices,
22702345
};
22712346

22722347
static struct target_type snapshot_target = {
22732348
.name = "snapshot",
2274-
.version = {1, 12, 0},
2349+
.version = {1, 13, 0},
22752350
.module = THIS_MODULE,
22762351
.ctr = snapshot_ctr,
22772352
.dtr = snapshot_dtr,

drivers/md/dm.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3121,6 +3121,7 @@ void dm_internal_suspend_fast(struct mapped_device *md)
31213121
flush_workqueue(md->wq);
31223122
dm_wait_for_completion(md, TASK_UNINTERRUPTIBLE);
31233123
}
3124+
EXPORT_SYMBOL_GPL(dm_internal_suspend_fast);
31243125

31253126
void dm_internal_resume_fast(struct mapped_device *md)
31263127
{
@@ -3132,6 +3133,7 @@ void dm_internal_resume_fast(struct mapped_device *md)
31323133
done:
31333134
mutex_unlock(&md->suspend_lock);
31343135
}
3136+
EXPORT_SYMBOL_GPL(dm_internal_resume_fast);
31353137

31363138
/*-----------------------------------------------------------------
31373139
* Event notification.

0 commit comments

Comments
 (0)