diff options
-rw-r--r-- | drivers/md/dm-linear.c | 1 | ||||
-rw-r--r-- | drivers/md/dm-table.c | 44 | ||||
-rw-r--r-- | drivers/md/dm.c | 65 | ||||
-rw-r--r-- | drivers/md/dm.h | 1 | ||||
-rw-r--r-- | include/linux/device-mapper.h | 6 |
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 | ||
1295 | static 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 | |||
1303 | bool 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 | |||
1284 | EXPORT_SYMBOL(dm_vcalloc); | 1328 | EXPORT_SYMBOL(dm_vcalloc); |
1285 | EXPORT_SYMBOL(dm_get_device); | 1329 | EXPORT_SYMBOL(dm_get_device); |
1286 | EXPORT_SYMBOL(dm_put_device); | 1330 | EXPORT_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 | */ | ||
1218 | static 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 | |||
1231 | static 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 | |||
1215 | static int __clone_and_map(struct clone_info *ci) | 1262 | static 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); | |||
61 | int dm_table_any_busy_target(struct dm_table *t); | 61 | int dm_table_any_busy_target(struct dm_table *t); |
62 | unsigned dm_table_get_type(struct dm_table *t); | 62 | unsigned dm_table_get_type(struct dm_table *t); |
63 | bool dm_table_request_based(struct dm_table *t); | 63 | bool dm_table_request_based(struct dm_table *t); |
64 | bool dm_table_supports_discards(struct dm_table *t); | ||
64 | int dm_table_alloc_md_mempools(struct dm_table *t); | 65 | int dm_table_alloc_md_mempools(struct dm_table *t); |
65 | void dm_table_free_md_mempools(struct dm_table *t); | 66 | void dm_table_free_md_mempools(struct dm_table *t); |
66 | struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t); | 67 | struct 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 | ||