aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/md/dm-linear.c1
-rw-r--r--drivers/md/dm-table.c44
-rw-r--r--drivers/md/dm.c65
-rw-r--r--drivers/md/dm.h1
-rw-r--r--include/linux/device-mapper.h6
5 files changed, 105 insertions, 12 deletions
diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c
index 9200dbf2391a..f043b5f433b2 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -53,6 +53,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
53 } 53 }
54 54
55 ti->num_flush_requests = 1; 55 ti->num_flush_requests = 1;
56 ti->num_discard_requests = 1;
56 ti->private = lc; 57 ti->private = lc;
57 return 0; 58 return 0;
58 59
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index bc60ef77a0d8..f9fc07d7a4b9 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -54,6 +54,8 @@ struct dm_table {
54 sector_t *highs; 54 sector_t *highs;
55 struct dm_target *targets; 55 struct dm_target *targets;
56 56
57 unsigned discards_supported:1;
58
57 /* 59 /*
58 * Indicates the rw permissions for the new logical 60 * Indicates the rw permissions for the new logical
59 * device. This should be a combination of FMODE_READ 61 * device. This should be a combination of FMODE_READ
@@ -203,6 +205,7 @@ int dm_table_create(struct dm_table **result, fmode_t mode,
203 205
204 INIT_LIST_HEAD(&t->devices); 206 INIT_LIST_HEAD(&t->devices);
205 atomic_set(&t->holders, 0); 207 atomic_set(&t->holders, 0);
208 t->discards_supported = 1;
206 209
207 if (!num_targets) 210 if (!num_targets)
208 num_targets = KEYS_PER_NODE; 211 num_targets = KEYS_PER_NODE;
@@ -770,6 +773,9 @@ int dm_table_add_target(struct dm_table *t, const char *type,
770 773
771 t->highs[t->num_targets++] = tgt->begin + tgt->len - 1; 774 t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;
772 775
776 if (!tgt->num_discard_requests)
777 t->discards_supported = 0;
778
773 return 0; 779 return 0;
774 780
775 bad: 781 bad:
@@ -1135,6 +1141,11 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
1135 else 1141 else
1136 queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, q); 1142 queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, q);
1137 1143
1144 if (!dm_table_supports_discards(t))
1145 queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q);
1146 else
1147 queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
1148
1138 dm_table_set_integrity(t); 1149 dm_table_set_integrity(t);
1139 1150
1140 /* 1151 /*
@@ -1281,6 +1292,39 @@ struct mapped_device *dm_table_get_md(struct dm_table *t)
1281 return t->md; 1292 return t->md;
1282} 1293}
1283 1294
1295static int device_discard_capable(struct dm_target *ti, struct dm_dev *dev,
1296 sector_t start, sector_t len, void *data)
1297{
1298 struct request_queue *q = bdev_get_queue(dev->bdev);
1299
1300 return q && blk_queue_discard(q);
1301}
1302
1303bool dm_table_supports_discards(struct dm_table *t)
1304{
1305 struct dm_target *ti;
1306 unsigned i = 0;
1307
1308 if (!t->discards_supported)
1309 return 0;
1310
1311 /*
1312 * Ensure that at least one underlying device supports discards.
1313 * t->devices includes internal dm devices such as mirror logs
1314 * so we need to use iterate_devices here, which targets
1315 * supporting discard must provide.
1316 */
1317 while (i < dm_table_get_num_targets(t)) {
1318 ti = dm_table_get_target(t, i++);
1319
1320 if (ti->type->iterate_devices &&
1321 ti->type->iterate_devices(ti, device_discard_capable, NULL))
1322 return 1;
1323 }
1324
1325 return 0;
1326}
1327
1284EXPORT_SYMBOL(dm_vcalloc); 1328EXPORT_SYMBOL(dm_vcalloc);
1285EXPORT_SYMBOL(dm_get_device); 1329EXPORT_SYMBOL(dm_get_device);
1286EXPORT_SYMBOL(dm_put_device); 1330EXPORT_SYMBOL(dm_put_device);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 0d4710175885..44aba29154fc 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1212,6 +1212,53 @@ static int __clone_and_map_empty_barrier(struct clone_info *ci)
1212 return 0; 1212 return 0;
1213} 1213}
1214 1214
1215/*
1216 * Perform all io with a single clone.
1217 */
1218static void __clone_and_map_simple(struct clone_info *ci, struct dm_target *ti)
1219{
1220 struct bio *clone, *bio = ci->bio;
1221 struct dm_target_io *tio;
1222
1223 tio = alloc_tio(ci, ti);
1224 clone = clone_bio(bio, ci->sector, ci->idx,
1225 bio->bi_vcnt - ci->idx, ci->sector_count,
1226 ci->md->bs);
1227 __map_bio(ti, clone, tio);
1228 ci->sector_count = 0;
1229}
1230
1231static int __clone_and_map_discard(struct clone_info *ci)
1232{
1233 struct dm_target *ti;
1234 sector_t max;
1235
1236 ti = dm_table_find_target(ci->map, ci->sector);
1237 if (!dm_target_is_valid(ti))
1238 return -EIO;
1239
1240 /*
1241 * Even though the device advertised discard support,
1242 * reconfiguration might have changed that since the
1243 * check was performed.
1244 */
1245
1246 if (!ti->num_discard_requests)
1247 return -EOPNOTSUPP;
1248
1249 max = max_io_len(ci->md, ci->sector, ti);
1250
1251 if (ci->sector_count > max)
1252 /*
1253 * FIXME: Handle a discard that spans two or more targets.
1254 */
1255 return -EOPNOTSUPP;
1256
1257 __clone_and_map_simple(ci, ti);
1258
1259 return 0;
1260}
1261
1215static int __clone_and_map(struct clone_info *ci) 1262static int __clone_and_map(struct clone_info *ci)
1216{ 1263{
1217 struct bio *clone, *bio = ci->bio; 1264 struct bio *clone, *bio = ci->bio;
@@ -1222,27 +1269,21 @@ static int __clone_and_map(struct clone_info *ci)
1222 if (unlikely(bio_empty_barrier(bio))) 1269 if (unlikely(bio_empty_barrier(bio)))
1223 return __clone_and_map_empty_barrier(ci); 1270 return __clone_and_map_empty_barrier(ci);
1224 1271
1272 if (unlikely(bio->bi_rw & REQ_DISCARD))
1273 return __clone_and_map_discard(ci);
1274
1225 ti = dm_table_find_target(ci->map, ci->sector); 1275 ti = dm_table_find_target(ci->map, ci->sector);
1226 if (!dm_target_is_valid(ti)) 1276 if (!dm_target_is_valid(ti))
1227 return -EIO; 1277 return -EIO;
1228 1278
1229 max = max_io_len(ci->md, ci->sector, ti); 1279 max = max_io_len(ci->md, ci->sector, ti);
1230 1280
1231 /*
1232 * Allocate a target io object.
1233 */
1234 tio = alloc_tio(ci, ti);
1235
1236 if (ci->sector_count <= max) { 1281 if (ci->sector_count <= max) {
1237 /* 1282 /*
1238 * Optimise for the simple case where we can do all of 1283 * Optimise for the simple case where we can do all of
1239 * the remaining io with a single clone. 1284 * the remaining io with a single clone.
1240 */ 1285 */
1241 clone = clone_bio(bio, ci->sector, ci->idx, 1286 __clone_and_map_simple(ci, ti);
1242 bio->bi_vcnt - ci->idx, ci->sector_count,
1243 ci->md->bs);
1244 __map_bio(ti, clone, tio);
1245 ci->sector_count = 0;
1246 1287
1247 } else if (to_sector(bio->bi_io_vec[ci->idx].bv_len) <= max) { 1288 } else if (to_sector(bio->bi_io_vec[ci->idx].bv_len) <= max) {
1248 /* 1289 /*
@@ -1263,6 +1304,7 @@ static int __clone_and_map(struct clone_info *ci)
1263 len += bv_len; 1304 len += bv_len;
1264 } 1305 }
1265 1306
1307 tio = alloc_tio(ci, ti);
1266 clone = clone_bio(bio, ci->sector, ci->idx, i - ci->idx, len, 1308 clone = clone_bio(bio, ci->sector, ci->idx, i - ci->idx, len,
1267 ci->md->bs); 1309 ci->md->bs);
1268 __map_bio(ti, clone, tio); 1310 __map_bio(ti, clone, tio);
@@ -1286,12 +1328,11 @@ static int __clone_and_map(struct clone_info *ci)
1286 return -EIO; 1328 return -EIO;
1287 1329
1288 max = max_io_len(ci->md, ci->sector, ti); 1330 max = max_io_len(ci->md, ci->sector, ti);
1289
1290 tio = alloc_tio(ci, ti);
1291 } 1331 }
1292 1332
1293 len = min(remaining, max); 1333 len = min(remaining, max);
1294 1334
1335 tio = alloc_tio(ci, ti);
1295 clone = split_bvec(bio, ci->sector, ci->idx, 1336 clone = split_bvec(bio, ci->sector, ci->idx,
1296 bv->bv_offset + offset, len, 1337 bv->bv_offset + offset, len,
1297 ci->md->bs); 1338 ci->md->bs);
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 0d7b374c5dc2..0c2dd5f4af76 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -61,6 +61,7 @@ int dm_table_any_congested(struct dm_table *t, int bdi_bits);
61int dm_table_any_busy_target(struct dm_table *t); 61int dm_table_any_busy_target(struct dm_table *t);
62unsigned dm_table_get_type(struct dm_table *t); 62unsigned dm_table_get_type(struct dm_table *t);
63bool dm_table_request_based(struct dm_table *t); 63bool dm_table_request_based(struct dm_table *t);
64bool dm_table_supports_discards(struct dm_table *t);
64int dm_table_alloc_md_mempools(struct dm_table *t); 65int dm_table_alloc_md_mempools(struct dm_table *t);
65void dm_table_free_md_mempools(struct dm_table *t); 66void dm_table_free_md_mempools(struct dm_table *t);
66struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t); 67struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t);
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 531a6f2635ae..751ce21dea7b 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -180,6 +180,12 @@ struct dm_target {
180 */ 180 */
181 unsigned num_flush_requests; 181 unsigned num_flush_requests;
182 182
183 /*
184 * The number of discard requests that will be submitted to the
185 * target. map_info->request_nr is used just like num_flush_requests.
186 */
187 unsigned num_discard_requests;
188
183 /* target specific data */ 189 /* target specific data */
184 void *private; 190 void *private;
185 191