11/*
2- * dm-snapshot.c
3- *
42 * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
53 *
64 * This file is released under the GPL.
@@ -134,7 +132,10 @@ struct dm_snapshot {
134132 * - I/O error while merging
135133 * => stop merging; set merge_failed; process I/O normally.
136134 */
137- int merge_failed ;
135+ bool merge_failed :1 ;
136+
137+ bool discard_zeroes_cow :1 ;
138+ bool discard_passdown_origin :1 ;
138139
139140 /*
140141 * Incoming bios that overlap with chunks being merged must wait
@@ -1173,21 +1174,73 @@ static void stop_merge(struct dm_snapshot *s)
11731174 clear_bit (SHUTDOWN_MERGE , & s -> state_bits );
11741175}
11751176
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+
11761227/*
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>]*]
11781230 */
11791231static int snapshot_ctr (struct dm_target * ti , unsigned int argc , char * * argv )
11801232{
11811233 struct dm_snapshot * s ;
1234+ struct dm_arg_set as ;
11821235 int i ;
11831236 int r = - EINVAL ;
11841237 char * origin_path , * cow_path ;
11851238 dev_t origin_dev , cow_dev ;
11861239 unsigned args_used , num_flush_bios = 1 ;
11871240 fmode_t origin_mode = FMODE_READ ;
11881241
1189- if (argc != 4 ) {
1190- ti -> error = "requires exactly 4 arguments" ;
1242+ if (argc < 4 ) {
1243+ ti -> error = "requires 4 or more arguments" ;
11911244 r = - EINVAL ;
11921245 goto bad ;
11931246 }
@@ -1204,6 +1257,13 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
12041257 goto bad ;
12051258 }
12061259
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+
12071267 origin_path = argv [0 ];
12081268 argv ++ ;
12091269 argc -- ;
@@ -1289,6 +1349,8 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
12891349
12901350 ti -> private = s ;
12911351 ti -> num_flush_bios = num_flush_bios ;
1352+ if (s -> discard_zeroes_cow )
1353+ ti -> num_discard_bios = (s -> discard_passdown_origin ? 2 : 1 );
12921354 ti -> per_io_data_size = sizeof (struct dm_snap_tracked_chunk );
12931355
12941356 /* 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)
13361398
13371399bad_read_metadata :
13381400 unregister_snapshot (s );
1339-
13401401bad_load_and_register :
13411402 mempool_exit (& s -> pending_pool );
1342-
13431403bad_pending_pool :
13441404 dm_kcopyd_client_destroy (s -> kcopyd_client );
1345-
13461405bad_kcopyd :
13471406 dm_exception_table_exit (& s -> pending , pending_cache );
13481407 dm_exception_table_exit (& s -> complete , exception_cache );
1349-
13501408bad_hash_tables :
13511409 dm_exception_store_destroy (s -> store );
1352-
13531410bad_store :
13541411 dm_put_device (ti , s -> cow );
1355-
13561412bad_cow :
13571413 dm_put_device (ti , s -> origin );
1358-
13591414bad_origin :
1415+ bad_features :
13601416 kfree (s );
1361-
13621417bad :
13631418 return r ;
13641419}
@@ -1806,6 +1861,37 @@ static void remap_exception(struct dm_snapshot *s, struct dm_exception *e,
18061861 (bio -> bi_iter .bi_sector & s -> store -> chunk_mask );
18071862}
18081863
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+
18091895static int snapshot_map (struct dm_target * ti , struct bio * bio )
18101896{
18111897 struct dm_exception * e ;
@@ -1839,10 +1925,43 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
18391925 goto out_unlock ;
18401926 }
18411927
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+
18421943 /* If the block is already remapped - use that, else remap it */
18431944 e = dm_lookup_exception (& s -> complete , chunk );
18441945 if (e ) {
18451946 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 ;
18461965 goto out_unlock ;
18471966 }
18481967
@@ -1890,9 +2009,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
18902009
18912010 r = DM_MAPIO_SUBMITTED ;
18922011
1893- if (!pe -> started &&
1894- bio -> bi_iter .bi_size ==
1895- (s -> store -> chunk_size << SECTOR_SHIFT )) {
2012+ if (!pe -> started && io_overlaps_chunk (s , bio )) {
18962013 pe -> started = 1 ;
18972014
18982015 dm_exception_table_unlock (& lock );
@@ -2138,6 +2255,7 @@ static void snapshot_status(struct dm_target *ti, status_type_t type,
21382255{
21392256 unsigned sz = 0 ;
21402257 struct dm_snapshot * snap = ti -> private ;
2258+ unsigned num_features ;
21412259
21422260 switch (type ) {
21432261 case STATUSTYPE_INFO :
@@ -2178,8 +2296,16 @@ static void snapshot_status(struct dm_target *ti, status_type_t type,
21782296 * make sense.
21792297 */
21802298 DMEMIT ("%s %s" , snap -> origin -> name , snap -> cow -> name );
2181- snap -> store -> type -> status (snap -> store , type , result + sz ,
2182- maxlen - sz );
2299+ sz += snap -> store -> type -> status (snap -> store , type , result + 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+ }
21832309 break ;
21842310 }
21852311}
@@ -2198,6 +2324,22 @@ static int snapshot_iterate_devices(struct dm_target *ti,
21982324 return r ;
21992325}
22002326
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+ }
22012343
22022344/*-----------------------------------------------------------------
22032345 * Origin methods
@@ -2522,7 +2664,7 @@ static struct target_type origin_target = {
25222664
25232665static struct target_type snapshot_target = {
25242666 .name = "snapshot" ,
2525- .version = {1 , 15 , 0 },
2667+ .version = {1 , 16 , 0 },
25262668 .module = THIS_MODULE ,
25272669 .ctr = snapshot_ctr ,
25282670 .dtr = snapshot_dtr ,
@@ -2532,11 +2674,12 @@ static struct target_type snapshot_target = {
25322674 .resume = snapshot_resume ,
25332675 .status = snapshot_status ,
25342676 .iterate_devices = snapshot_iterate_devices ,
2677+ .io_hints = snapshot_io_hints ,
25352678};
25362679
25372680static struct target_type merge_target = {
25382681 .name = dm_snapshot_merge_target_name ,
2539- .version = {1 , 4 , 0 },
2682+ .version = {1 , 5 , 0 },
25402683 .module = THIS_MODULE ,
25412684 .ctr = snapshot_ctr ,
25422685 .dtr = snapshot_dtr ,
@@ -2547,6 +2690,7 @@ static struct target_type merge_target = {
25472690 .resume = snapshot_merge_resume ,
25482691 .status = snapshot_status ,
25492692 .iterate_devices = snapshot_iterate_devices ,
2693+ .io_hints = snapshot_io_hints ,
25502694};
25512695
25522696static int __init dm_snapshot_init (void )
0 commit comments