diff options
| -rw-r--r-- | Documentation/device-mapper/snapshot.txt | 16 | ||||
| -rw-r--r-- | drivers/md/dm-snap.c | 186 |
2 files changed, 181 insertions, 21 deletions
diff --git a/Documentation/device-mapper/snapshot.txt b/Documentation/device-mapper/snapshot.txt index b8bbb516f989..1810833f6dc6 100644 --- a/Documentation/device-mapper/snapshot.txt +++ b/Documentation/device-mapper/snapshot.txt | |||
| @@ -31,6 +31,7 @@ its visible content unchanged, at least until the <COW device> fills up. | |||
| 31 | 31 | ||
| 32 | 32 | ||
| 33 | *) snapshot <origin> <COW device> <persistent?> <chunksize> | 33 | *) snapshot <origin> <COW device> <persistent?> <chunksize> |
| 34 | [<# feature args> [<arg>]*] | ||
| 34 | 35 | ||
| 35 | A snapshot of the <origin> block device is created. Changed chunks of | 36 | A snapshot of the <origin> block device is created. Changed chunks of |
| 36 | <chunksize> sectors will be stored on the <COW device>. Writes will | 37 | <chunksize> sectors will be stored on the <COW device>. Writes will |
| @@ -53,8 +54,23 @@ When loading or unloading the snapshot target, the corresponding | |||
| 53 | snapshot-origin or snapshot-merge target must be suspended. A failure to | 54 | snapshot-origin or snapshot-merge target must be suspended. A failure to |
| 54 | suspend the origin target could result in data corruption. | 55 | suspend the origin target could result in data corruption. |
| 55 | 56 | ||
| 57 | Optional features: | ||
| 58 | |||
| 59 | discard_zeroes_cow - a discard issued to the snapshot device that | ||
| 60 | maps to entire chunks to will zero the corresponding exception(s) in | ||
| 61 | the snapshot's exception store. | ||
| 62 | |||
| 63 | discard_passdown_origin - a discard to the snapshot device is passed | ||
| 64 | down to the snapshot-origin's underlying device. This doesn't cause | ||
| 65 | copy-out to the snapshot exception store because the snapshot-origin | ||
| 66 | target is bypassed. | ||
| 67 | |||
| 68 | The discard_passdown_origin feature depends on the discard_zeroes_cow | ||
| 69 | feature being enabled. | ||
| 70 | |||
| 56 | 71 | ||
| 57 | * snapshot-merge <origin> <COW device> <persistent> <chunksize> | 72 | * snapshot-merge <origin> <COW device> <persistent> <chunksize> |
| 73 | [<# feature args> [<arg>]*] | ||
| 58 | 74 | ||
| 59 | takes the same table arguments as the snapshot target except it only | 75 | takes the same table arguments as the snapshot target except it only |
| 60 | works with persistent snapshots. This target assumes the role of the | 76 | works with persistent snapshots. This target assumes the role of the |
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 3107f2b1988b..63916e1dc569 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c | |||
| @@ -1,6 +1,4 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * dm-snapshot.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2001-2002 Sistina Software (UK) Limited. | 2 | * Copyright (C) 2001-2002 Sistina Software (UK) Limited. |
| 5 | * | 3 | * |
| 6 | * This file is released under the GPL. | 4 | * This file is released under the GPL. |
| @@ -134,7 +132,10 @@ struct dm_snapshot { | |||
| 134 | * - I/O error while merging | 132 | * - I/O error while merging |
| 135 | * => stop merging; set merge_failed; process I/O normally. | 133 | * => stop merging; set merge_failed; process I/O normally. |
| 136 | */ | 134 | */ |
| 137 | int merge_failed; | 135 | bool merge_failed:1; |
| 136 | |||
| 137 | bool discard_zeroes_cow:1; | ||
| 138 | bool discard_passdown_origin:1; | ||
| 138 | 139 | ||
| 139 | /* | 140 | /* |
| 140 | * Incoming bios that overlap with chunks being merged must wait | 141 | * Incoming bios that overlap with chunks being merged must wait |
| @@ -1173,12 +1174,64 @@ static void stop_merge(struct dm_snapshot *s) | |||
| 1173 | clear_bit(SHUTDOWN_MERGE, &s->state_bits); | 1174 | clear_bit(SHUTDOWN_MERGE, &s->state_bits); |
| 1174 | } | 1175 | } |
| 1175 | 1176 | ||
| 1177 | static int parse_snapshot_features(struct dm_arg_set *as, struct dm_snapshot *s, | ||
| 1178 | struct dm_target *ti) | ||
| 1179 | { | ||
| 1180 | int r; | ||
| 1181 | unsigned argc; | ||
| 1182 | const char *arg_name; | ||
| 1183 | |||
| 1184 | static const struct dm_arg _args[] = { | ||
| 1185 | {0, 2, "Invalid number of feature arguments"}, | ||
| 1186 | }; | ||
| 1187 | |||
| 1188 | /* | ||
| 1189 | * No feature arguments supplied. | ||
| 1190 | */ | ||
| 1191 | if (!as->argc) | ||
| 1192 | return 0; | ||
| 1193 | |||
| 1194 | r = dm_read_arg_group(_args, as, &argc, &ti->error); | ||
| 1195 | if (r) | ||
| 1196 | return -EINVAL; | ||
| 1197 | |||
| 1198 | while (argc && !r) { | ||
| 1199 | arg_name = dm_shift_arg(as); | ||
| 1200 | argc--; | ||
| 1201 | |||
| 1202 | if (!strcasecmp(arg_name, "discard_zeroes_cow")) | ||
| 1203 | s->discard_zeroes_cow = true; | ||
| 1204 | |||
| 1205 | else if (!strcasecmp(arg_name, "discard_passdown_origin")) | ||
| 1206 | s->discard_passdown_origin = true; | ||
| 1207 | |||
| 1208 | else { | ||
| 1209 | ti->error = "Unrecognised feature requested"; | ||
| 1210 | r = -EINVAL; | ||
| 1211 | break; | ||
| 1212 | } | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | if (!s->discard_zeroes_cow && s->discard_passdown_origin) { | ||
| 1216 | /* | ||
| 1217 | * TODO: really these are disjoint.. but ti->num_discard_bios | ||
| 1218 | * and dm_bio_get_target_bio_nr() require rigid constraints. | ||
| 1219 | */ | ||
| 1220 | ti->error = "discard_passdown_origin feature depends on discard_zeroes_cow"; | ||
| 1221 | r = -EINVAL; | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | return r; | ||
| 1225 | } | ||
| 1226 | |||
| 1176 | /* | 1227 | /* |
| 1177 | * Construct a snapshot mapping: <origin_dev> <COW-dev> <p|po|n> <chunk-size> | 1228 | * Construct a snapshot mapping: |
| 1229 | * <origin_dev> <COW-dev> <p|po|n> <chunk-size> [<# feature args> [<arg>]*] | ||
| 1178 | */ | 1230 | */ |
| 1179 | static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | 1231 | static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) |
| 1180 | { | 1232 | { |
| 1181 | struct dm_snapshot *s; | 1233 | struct dm_snapshot *s; |
| 1234 | struct dm_arg_set as; | ||
| 1182 | int i; | 1235 | int i; |
| 1183 | int r = -EINVAL; | 1236 | int r = -EINVAL; |
| 1184 | char *origin_path, *cow_path; | 1237 | char *origin_path, *cow_path; |
| @@ -1186,8 +1239,8 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
| 1186 | unsigned args_used, num_flush_bios = 1; | 1239 | unsigned args_used, num_flush_bios = 1; |
| 1187 | fmode_t origin_mode = FMODE_READ; | 1240 | fmode_t origin_mode = FMODE_READ; |
| 1188 | 1241 | ||
| 1189 | if (argc != 4) { | 1242 | if (argc < 4) { |
| 1190 | ti->error = "requires exactly 4 arguments"; | 1243 | ti->error = "requires 4 or more arguments"; |
| 1191 | r = -EINVAL; | 1244 | r = -EINVAL; |
| 1192 | goto bad; | 1245 | goto bad; |
| 1193 | } | 1246 | } |
| @@ -1204,6 +1257,13 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
| 1204 | goto bad; | 1257 | goto bad; |
| 1205 | } | 1258 | } |
| 1206 | 1259 | ||
| 1260 | as.argc = argc; | ||
| 1261 | as.argv = argv; | ||
| 1262 | dm_consume_args(&as, 4); | ||
| 1263 | r = parse_snapshot_features(&as, s, ti); | ||
| 1264 | if (r) | ||
| 1265 | goto bad_features; | ||
| 1266 | |||
| 1207 | origin_path = argv[0]; | 1267 | origin_path = argv[0]; |
| 1208 | argv++; | 1268 | argv++; |
| 1209 | argc--; | 1269 | argc--; |
| @@ -1289,6 +1349,8 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
| 1289 | 1349 | ||
| 1290 | ti->private = s; | 1350 | ti->private = s; |
| 1291 | ti->num_flush_bios = num_flush_bios; | 1351 | ti->num_flush_bios = num_flush_bios; |
| 1352 | if (s->discard_zeroes_cow) | ||
| 1353 | ti->num_discard_bios = (s->discard_passdown_origin ? 2 : 1); | ||
| 1292 | ti->per_io_data_size = sizeof(struct dm_snap_tracked_chunk); | 1354 | ti->per_io_data_size = sizeof(struct dm_snap_tracked_chunk); |
| 1293 | 1355 | ||
| 1294 | /* Add snapshot to the list of snapshots for this origin */ | 1356 | /* Add snapshot to the list of snapshots for this origin */ |
| @@ -1336,29 +1398,22 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
| 1336 | 1398 | ||
| 1337 | bad_read_metadata: | 1399 | bad_read_metadata: |
| 1338 | unregister_snapshot(s); | 1400 | unregister_snapshot(s); |
| 1339 | |||
| 1340 | bad_load_and_register: | 1401 | bad_load_and_register: |
| 1341 | mempool_exit(&s->pending_pool); | 1402 | mempool_exit(&s->pending_pool); |
| 1342 | |||
| 1343 | bad_pending_pool: | 1403 | bad_pending_pool: |
| 1344 | dm_kcopyd_client_destroy(s->kcopyd_client); | 1404 | dm_kcopyd_client_destroy(s->kcopyd_client); |
| 1345 | |||
| 1346 | bad_kcopyd: | 1405 | bad_kcopyd: |
| 1347 | dm_exception_table_exit(&s->pending, pending_cache); | 1406 | dm_exception_table_exit(&s->pending, pending_cache); |
| 1348 | dm_exception_table_exit(&s->complete, exception_cache); | 1407 | dm_exception_table_exit(&s->complete, exception_cache); |
| 1349 | |||
| 1350 | bad_hash_tables: | 1408 | bad_hash_tables: |
| 1351 | dm_exception_store_destroy(s->store); | 1409 | dm_exception_store_destroy(s->store); |
| 1352 | |||
| 1353 | bad_store: | 1410 | bad_store: |
| 1354 | dm_put_device(ti, s->cow); | 1411 | dm_put_device(ti, s->cow); |
| 1355 | |||
| 1356 | bad_cow: | 1412 | bad_cow: |
| 1357 | dm_put_device(ti, s->origin); | 1413 | dm_put_device(ti, s->origin); |
| 1358 | |||
| 1359 | bad_origin: | 1414 | bad_origin: |
| 1415 | bad_features: | ||
| 1360 | kfree(s); | 1416 | kfree(s); |
| 1361 | |||
| 1362 | bad: | 1417 | bad: |
| 1363 | return r; | 1418 | return r; |
| 1364 | } | 1419 | } |
| @@ -1806,6 +1861,37 @@ static void remap_exception(struct dm_snapshot *s, struct dm_exception *e, | |||
| 1806 | (bio->bi_iter.bi_sector & s->store->chunk_mask); | 1861 | (bio->bi_iter.bi_sector & s->store->chunk_mask); |
| 1807 | } | 1862 | } |
| 1808 | 1863 | ||
| 1864 | static void zero_callback(int read_err, unsigned long write_err, void *context) | ||
| 1865 | { | ||
| 1866 | struct bio *bio = context; | ||
| 1867 | struct dm_snapshot *s = bio->bi_private; | ||
| 1868 | |||
| 1869 | up(&s->cow_count); | ||
| 1870 | bio->bi_status = write_err ? BLK_STS_IOERR : 0; | ||
| 1871 | bio_endio(bio); | ||
| 1872 | } | ||
| 1873 | |||
| 1874 | static void zero_exception(struct dm_snapshot *s, struct dm_exception *e, | ||
| 1875 | struct bio *bio, chunk_t chunk) | ||
| 1876 | { | ||
| 1877 | struct dm_io_region dest; | ||
| 1878 | |||
| 1879 | dest.bdev = s->cow->bdev; | ||
| 1880 | dest.sector = bio->bi_iter.bi_sector; | ||
| 1881 | dest.count = s->store->chunk_size; | ||
| 1882 | |||
| 1883 | down(&s->cow_count); | ||
| 1884 | WARN_ON_ONCE(bio->bi_private); | ||
| 1885 | bio->bi_private = s; | ||
| 1886 | dm_kcopyd_zero(s->kcopyd_client, 1, &dest, 0, zero_callback, bio); | ||
| 1887 | } | ||
| 1888 | |||
| 1889 | static bool io_overlaps_chunk(struct dm_snapshot *s, struct bio *bio) | ||
| 1890 | { | ||
| 1891 | return bio->bi_iter.bi_size == | ||
| 1892 | (s->store->chunk_size << SECTOR_SHIFT); | ||
| 1893 | } | ||
| 1894 | |||
| 1809 | static int snapshot_map(struct dm_target *ti, struct bio *bio) | 1895 | static int snapshot_map(struct dm_target *ti, struct bio *bio) |
| 1810 | { | 1896 | { |
| 1811 | struct dm_exception *e; | 1897 | struct dm_exception *e; |
| @@ -1839,10 +1925,43 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio) | |||
| 1839 | goto out_unlock; | 1925 | goto out_unlock; |
| 1840 | } | 1926 | } |
| 1841 | 1927 | ||
| 1928 | if (unlikely(bio_op(bio) == REQ_OP_DISCARD)) { | ||
| 1929 | if (s->discard_passdown_origin && dm_bio_get_target_bio_nr(bio)) { | ||
| 1930 | /* | ||
| 1931 | * passdown discard to origin (without triggering | ||
| 1932 | * snapshot exceptions via do_origin; doing so would | ||
| 1933 | * defeat the goal of freeing space in origin that is | ||
| 1934 | * implied by the "discard_passdown_origin" feature) | ||
| 1935 | */ | ||
| 1936 | bio_set_dev(bio, s->origin->bdev); | ||
| 1937 | track_chunk(s, bio, chunk); | ||
| 1938 | goto out_unlock; | ||
| 1939 | } | ||
| 1940 | /* discard to snapshot (target_bio_nr == 0) zeroes exceptions */ | ||
| 1941 | } | ||
| 1942 | |||
| 1842 | /* If the block is already remapped - use that, else remap it */ | 1943 | /* If the block is already remapped - use that, else remap it */ |
| 1843 | e = dm_lookup_exception(&s->complete, chunk); | 1944 | e = dm_lookup_exception(&s->complete, chunk); |
| 1844 | if (e) { | 1945 | if (e) { |
| 1845 | remap_exception(s, e, bio, chunk); | 1946 | remap_exception(s, e, bio, chunk); |
| 1947 | if (unlikely(bio_op(bio) == REQ_OP_DISCARD) && | ||
| 1948 | io_overlaps_chunk(s, bio)) { | ||
| 1949 | dm_exception_table_unlock(&lock); | ||
| 1950 | up_read(&s->lock); | ||
| 1951 | zero_exception(s, e, bio, chunk); | ||
| 1952 | r = DM_MAPIO_SUBMITTED; /* discard is not issued */ | ||
| 1953 | goto out; | ||
| 1954 | } | ||
| 1955 | goto out_unlock; | ||
| 1956 | } | ||
| 1957 | |||
| 1958 | if (unlikely(bio_op(bio) == REQ_OP_DISCARD)) { | ||
| 1959 | /* | ||
| 1960 | * If no exception exists, complete discard immediately | ||
| 1961 | * otherwise it'll trigger copy-out. | ||
| 1962 | */ | ||
| 1963 | bio_endio(bio); | ||
| 1964 | r = DM_MAPIO_SUBMITTED; | ||
| 1846 | goto out_unlock; | 1965 | goto out_unlock; |
| 1847 | } | 1966 | } |
| 1848 | 1967 | ||
| @@ -1890,9 +2009,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio) | |||
| 1890 | 2009 | ||
| 1891 | r = DM_MAPIO_SUBMITTED; | 2010 | r = DM_MAPIO_SUBMITTED; |
| 1892 | 2011 | ||
| 1893 | if (!pe->started && | 2012 | if (!pe->started && io_overlaps_chunk(s, bio)) { |
| 1894 | bio->bi_iter.bi_size == | ||
| 1895 | (s->store->chunk_size << SECTOR_SHIFT)) { | ||
| 1896 | pe->started = 1; | 2013 | pe->started = 1; |
| 1897 | 2014 | ||
| 1898 | dm_exception_table_unlock(&lock); | 2015 | dm_exception_table_unlock(&lock); |
| @@ -2138,6 +2255,7 @@ static void snapshot_status(struct dm_target *ti, status_type_t type, | |||
| 2138 | { | 2255 | { |
| 2139 | unsigned sz = 0; | 2256 | unsigned sz = 0; |
| 2140 | struct dm_snapshot *snap = ti->private; | 2257 | struct dm_snapshot *snap = ti->private; |
| 2258 | unsigned num_features; | ||
| 2141 | 2259 | ||
| 2142 | switch (type) { | 2260 | switch (type) { |
| 2143 | case STATUSTYPE_INFO: | 2261 | case STATUSTYPE_INFO: |
| @@ -2178,8 +2296,16 @@ static void snapshot_status(struct dm_target *ti, status_type_t type, | |||
| 2178 | * make sense. | 2296 | * make sense. |
| 2179 | */ | 2297 | */ |
| 2180 | DMEMIT("%s %s", snap->origin->name, snap->cow->name); | 2298 | DMEMIT("%s %s", snap->origin->name, snap->cow->name); |
| 2181 | snap->store->type->status(snap->store, type, result + sz, | 2299 | sz += snap->store->type->status(snap->store, type, result + sz, |
| 2182 | maxlen - sz); | 2300 | maxlen - sz); |
| 2301 | num_features = snap->discard_zeroes_cow + snap->discard_passdown_origin; | ||
| 2302 | if (num_features) { | ||
| 2303 | DMEMIT(" %u", num_features); | ||
| 2304 | if (snap->discard_zeroes_cow) | ||
| 2305 | DMEMIT(" discard_zeroes_cow"); | ||
| 2306 | if (snap->discard_passdown_origin) | ||
| 2307 | DMEMIT(" discard_passdown_origin"); | ||
| 2308 | } | ||
| 2183 | break; | 2309 | break; |
| 2184 | } | 2310 | } |
| 2185 | } | 2311 | } |
| @@ -2198,6 +2324,22 @@ static int snapshot_iterate_devices(struct dm_target *ti, | |||
| 2198 | return r; | 2324 | return r; |
| 2199 | } | 2325 | } |
| 2200 | 2326 | ||
| 2327 | static void snapshot_io_hints(struct dm_target *ti, struct queue_limits *limits) | ||
| 2328 | { | ||
| 2329 | struct dm_snapshot *snap = ti->private; | ||
| 2330 | |||
| 2331 | if (snap->discard_zeroes_cow) { | ||
| 2332 | struct dm_snapshot *snap_src = NULL, *snap_dest = NULL; | ||
| 2333 | |||
| 2334 | (void) __find_snapshots_sharing_cow(snap, &snap_src, &snap_dest, NULL); | ||
| 2335 | if (snap_src && snap_dest) | ||
| 2336 | snap = snap_src; | ||
| 2337 | |||
| 2338 | /* All discards are split on chunk_size boundary */ | ||
| 2339 | limits->discard_granularity = snap->store->chunk_size; | ||
| 2340 | limits->max_discard_sectors = snap->store->chunk_size; | ||
| 2341 | } | ||
| 2342 | } | ||
| 2201 | 2343 | ||
| 2202 | /*----------------------------------------------------------------- | 2344 | /*----------------------------------------------------------------- |
| 2203 | * Origin methods | 2345 | * Origin methods |
| @@ -2522,7 +2664,7 @@ static struct target_type origin_target = { | |||
| 2522 | 2664 | ||
| 2523 | static struct target_type snapshot_target = { | 2665 | static struct target_type snapshot_target = { |
| 2524 | .name = "snapshot", | 2666 | .name = "snapshot", |
| 2525 | .version = {1, 15, 0}, | 2667 | .version = {1, 16, 0}, |
| 2526 | .module = THIS_MODULE, | 2668 | .module = THIS_MODULE, |
| 2527 | .ctr = snapshot_ctr, | 2669 | .ctr = snapshot_ctr, |
| 2528 | .dtr = snapshot_dtr, | 2670 | .dtr = snapshot_dtr, |
| @@ -2532,11 +2674,12 @@ static struct target_type snapshot_target = { | |||
| 2532 | .resume = snapshot_resume, | 2674 | .resume = snapshot_resume, |
| 2533 | .status = snapshot_status, | 2675 | .status = snapshot_status, |
| 2534 | .iterate_devices = snapshot_iterate_devices, | 2676 | .iterate_devices = snapshot_iterate_devices, |
| 2677 | .io_hints = snapshot_io_hints, | ||
| 2535 | }; | 2678 | }; |
| 2536 | 2679 | ||
| 2537 | static struct target_type merge_target = { | 2680 | static struct target_type merge_target = { |
| 2538 | .name = dm_snapshot_merge_target_name, | 2681 | .name = dm_snapshot_merge_target_name, |
| 2539 | .version = {1, 4, 0}, | 2682 | .version = {1, 5, 0}, |
| 2540 | .module = THIS_MODULE, | 2683 | .module = THIS_MODULE, |
| 2541 | .ctr = snapshot_ctr, | 2684 | .ctr = snapshot_ctr, |
| 2542 | .dtr = snapshot_dtr, | 2685 | .dtr = snapshot_dtr, |
| @@ -2547,6 +2690,7 @@ static struct target_type merge_target = { | |||
| 2547 | .resume = snapshot_merge_resume, | 2690 | .resume = snapshot_merge_resume, |
| 2548 | .status = snapshot_status, | 2691 | .status = snapshot_status, |
| 2549 | .iterate_devices = snapshot_iterate_devices, | 2692 | .iterate_devices = snapshot_iterate_devices, |
| 2693 | .io_hints = snapshot_io_hints, | ||
| 2550 | }; | 2694 | }; |
| 2551 | 2695 | ||
| 2552 | static int __init dm_snapshot_init(void) | 2696 | static int __init dm_snapshot_init(void) |
