aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md
diff options
context:
space:
mode:
authorMikulas Patocka <mpatocka@redhat.com>2015-02-26 11:40:35 -0500
committerMike Snitzer <snitzer@redhat.com>2015-02-27 14:49:47 -0500
commitb735fede8d957d9d255e9c5cf3964cfa59799637 (patch)
treedc09f6c9db8d21128e1c5f793460eaaf5450c406 /drivers/md
parentab7c7bb6f4ab95dbca96fcfc4463cd69843e3e24 (diff)
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 fd2ed4d252701d3bbed4cd3e3d267ad469bb832a. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com> Cc: stable@vger.kernel.org
Diffstat (limited to 'drivers/md')
-rw-r--r--drivers/md/dm-snap.c93
-rw-r--r--drivers/md/dm.c2
2 files changed, 86 insertions, 9 deletions
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 8b204ae216ab..c2bf822bad6f 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -20,6 +20,8 @@
20#include <linux/log2.h> 20#include <linux/log2.h>
21#include <linux/dm-kcopyd.h> 21#include <linux/dm-kcopyd.h>
22 22
23#include "dm.h"
24
23#include "dm-exception-store.h" 25#include "dm-exception-store.h"
24 26
25#define DM_MSG_PREFIX "snapshots" 27#define DM_MSG_PREFIX "snapshots"
@@ -291,12 +293,23 @@ struct origin {
291}; 293};
292 294
293/* 295/*
296 * This structure is allocated for each origin target
297 */
298struct dm_origin {
299 struct dm_dev *dev;
300 struct dm_target *ti;
301 unsigned split_boundary;
302 struct list_head hash_list;
303};
304
305/*
294 * Size of the hash table for origin volumes. If we make this 306 * Size of the hash table for origin volumes. If we make this
295 * the size of the minors list then it should be nearly perfect 307 * the size of the minors list then it should be nearly perfect
296 */ 308 */
297#define ORIGIN_HASH_SIZE 256 309#define ORIGIN_HASH_SIZE 256
298#define ORIGIN_MASK 0xFF 310#define ORIGIN_MASK 0xFF
299static struct list_head *_origins; 311static struct list_head *_origins;
312static struct list_head *_dm_origins;
300static struct rw_semaphore _origins_lock; 313static struct rw_semaphore _origins_lock;
301 314
302static DECLARE_WAIT_QUEUE_HEAD(_pending_exceptions_done); 315static DECLARE_WAIT_QUEUE_HEAD(_pending_exceptions_done);
@@ -310,12 +323,22 @@ static int init_origin_hash(void)
310 _origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head), 323 _origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
311 GFP_KERNEL); 324 GFP_KERNEL);
312 if (!_origins) { 325 if (!_origins) {
313 DMERR("unable to allocate memory"); 326 DMERR("unable to allocate memory for _origins");
314 return -ENOMEM; 327 return -ENOMEM;
315 } 328 }
316
317 for (i = 0; i < ORIGIN_HASH_SIZE; i++) 329 for (i = 0; i < ORIGIN_HASH_SIZE; i++)
318 INIT_LIST_HEAD(_origins + i); 330 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
319 init_rwsem(&_origins_lock); 342 init_rwsem(&_origins_lock);
320 343
321 return 0; 344 return 0;
@@ -324,6 +347,7 @@ static int init_origin_hash(void)
324static void exit_origin_hash(void) 347static void exit_origin_hash(void)
325{ 348{
326 kfree(_origins); 349 kfree(_origins);
350 kfree(_dm_origins);
327} 351}
328 352
329static unsigned origin_hash(struct block_device *bdev) 353static unsigned origin_hash(struct block_device *bdev)
@@ -350,6 +374,30 @@ static void __insert_origin(struct origin *o)
350 list_add_tail(&o->hash_list, sl); 374 list_add_tail(&o->hash_list, sl);
351} 375}
352 376
377static 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
390static 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
396static void __remove_dm_origin(struct dm_origin *o)
397{
398 list_del(&o->hash_list);
399}
400
353/* 401/*
354 * _origins_lock must be held when calling this function. 402 * _origins_lock must be held when calling this function.
355 * Returns number of snapshots registered using the supplied cow device, plus: 403 * Returns number of snapshots registered using the supplied cow device, plus:
@@ -1841,8 +1889,20 @@ static void snapshot_resume(struct dm_target *ti)
1841{ 1889{
1842 struct dm_snapshot *s = ti->private; 1890 struct dm_snapshot *s = ti->private;
1843 struct dm_snapshot *snap_src = NULL, *snap_dest = NULL; 1891 struct dm_snapshot *snap_src = NULL, *snap_dest = NULL;
1892 struct dm_origin *o;
1893 struct mapped_device *origin_md = NULL;
1844 1894
1845 down_read(&_origins_lock); 1895 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
1846 (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL); 1906 (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL);
1847 if (snap_src && snap_dest) { 1907 if (snap_src && snap_dest) {
1848 down_write(&snap_src->lock); 1908 down_write(&snap_src->lock);
@@ -1851,6 +1911,10 @@ static void snapshot_resume(struct dm_target *ti)
1851 up_write(&snap_dest->lock); 1911 up_write(&snap_dest->lock);
1852 up_write(&snap_src->lock); 1912 up_write(&snap_src->lock);
1853 } 1913 }
1914
1915 if (origin_md)
1916 dm_internal_resume_fast(origin_md);
1917
1854 up_read(&_origins_lock); 1918 up_read(&_origins_lock);
1855 1919
1856 /* Now we have correct chunk size, reregister */ 1920 /* Now we have correct chunk size, reregister */
@@ -2133,11 +2197,6 @@ static int origin_write_extent(struct dm_snapshot *merging_snap,
2133 * Origin: maps a linear range of a device, with hooks for snapshotting. 2197 * Origin: maps a linear range of a device, with hooks for snapshotting.
2134 */ 2198 */
2135 2199
2136struct dm_origin {
2137 struct dm_dev *dev;
2138 unsigned split_boundary;
2139};
2140
2141/* 2200/*
2142 * Construct an origin mapping: <dev_path> 2201 * Construct an origin mapping: <dev_path>
2143 * The context for an origin is merely a 'struct dm_dev *' 2202 * 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)
2166 goto bad_open; 2225 goto bad_open;
2167 } 2226 }
2168 2227
2228 o->ti = ti;
2169 ti->private = o; 2229 ti->private = o;
2170 ti->num_flush_bios = 1; 2230 ti->num_flush_bios = 1;
2171 2231
@@ -2180,6 +2240,7 @@ bad_alloc:
2180static void origin_dtr(struct dm_target *ti) 2240static void origin_dtr(struct dm_target *ti)
2181{ 2241{
2182 struct dm_origin *o = ti->private; 2242 struct dm_origin *o = ti->private;
2243
2183 dm_put_device(ti, o->dev); 2244 dm_put_device(ti, o->dev);
2184 kfree(o); 2245 kfree(o);
2185} 2246}
@@ -2216,6 +2277,19 @@ static void origin_resume(struct dm_target *ti)
2216 struct dm_origin *o = ti->private; 2277 struct dm_origin *o = ti->private;
2217 2278
2218 o->split_boundary = get_origin_minimum_chunksize(o->dev->bdev); 2279 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
2286static 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);
2219} 2293}
2220 2294
2221static void origin_status(struct dm_target *ti, status_type_t type, 2295static void origin_status(struct dm_target *ti, status_type_t type,
@@ -2258,12 +2332,13 @@ static int origin_iterate_devices(struct dm_target *ti,
2258 2332
2259static struct target_type origin_target = { 2333static struct target_type origin_target = {
2260 .name = "snapshot-origin", 2334 .name = "snapshot-origin",
2261 .version = {1, 8, 1}, 2335 .version = {1, 9, 0},
2262 .module = THIS_MODULE, 2336 .module = THIS_MODULE,
2263 .ctr = origin_ctr, 2337 .ctr = origin_ctr,
2264 .dtr = origin_dtr, 2338 .dtr = origin_dtr,
2265 .map = origin_map, 2339 .map = origin_map,
2266 .resume = origin_resume, 2340 .resume = origin_resume,
2341 .postsuspend = origin_postsuspend,
2267 .status = origin_status, 2342 .status = origin_status,
2268 .merge = origin_merge, 2343 .merge = origin_merge,
2269 .iterate_devices = origin_iterate_devices, 2344 .iterate_devices = origin_iterate_devices,
@@ -2271,7 +2346,7 @@ static struct target_type origin_target = {
2271 2346
2272static struct target_type snapshot_target = { 2347static struct target_type snapshot_target = {
2273 .name = "snapshot", 2348 .name = "snapshot",
2274 .version = {1, 12, 0}, 2349 .version = {1, 13, 0},
2275 .module = THIS_MODULE, 2350 .module = THIS_MODULE,
2276 .ctr = snapshot_ctr, 2351 .ctr = snapshot_ctr,
2277 .dtr = snapshot_dtr, 2352 .dtr = snapshot_dtr,
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index e79d5ccfda64..6e2b2e97abe9 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -3121,6 +3121,7 @@ void dm_internal_suspend_fast(struct mapped_device *md)
3121 flush_workqueue(md->wq); 3121 flush_workqueue(md->wq);
3122 dm_wait_for_completion(md, TASK_UNINTERRUPTIBLE); 3122 dm_wait_for_completion(md, TASK_UNINTERRUPTIBLE);
3123} 3123}
3124EXPORT_SYMBOL_GPL(dm_internal_suspend_fast);
3124 3125
3125void dm_internal_resume_fast(struct mapped_device *md) 3126void dm_internal_resume_fast(struct mapped_device *md)
3126{ 3127{
@@ -3132,6 +3133,7 @@ void dm_internal_resume_fast(struct mapped_device *md)
3132done: 3133done:
3133 mutex_unlock(&md->suspend_lock); 3134 mutex_unlock(&md->suspend_lock);
3134} 3135}
3136EXPORT_SYMBOL_GPL(dm_internal_resume_fast);
3135 3137
3136/*----------------------------------------------------------------- 3138/*-----------------------------------------------------------------
3137 * Event notification. 3139 * Event notification.