diff options
author | Li Dongyang <lidongyang@novell.com> | 2011-09-01 06:39:09 -0400 |
---|---|---|
committer | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2011-10-13 09:48:31 -0400 |
commit | ed30bf317c5ceb25166cdbce3e0b35e33c82b509 (patch) | |
tree | 112e4e184a29e64b618f83da873333abae25f11e /drivers/block | |
parent | b3cb0d6adc4bbc70b5e37e49a6068e973545ead7 (diff) |
xen-blkfront: Handle discard requests.
If the backend advertises 'feature-discard', then interrogate
the backend for alignment and granularity. Setup the request
queue with the appropiate values and send the discard operation
as required.
Signed-off-by: Li Dongyang <lidongyang@novell.com>
[v1: Amended commit description]
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/xen-blkfront.c | 111 |
1 files changed, 88 insertions, 23 deletions
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index b536a9cef917..52076b0d0326 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c | |||
@@ -98,6 +98,9 @@ struct blkfront_info | |||
98 | unsigned long shadow_free; | 98 | unsigned long shadow_free; |
99 | unsigned int feature_flush; | 99 | unsigned int feature_flush; |
100 | unsigned int flush_op; | 100 | unsigned int flush_op; |
101 | unsigned int feature_discard; | ||
102 | unsigned int discard_granularity; | ||
103 | unsigned int discard_alignment; | ||
101 | int is_ready; | 104 | int is_ready; |
102 | }; | 105 | }; |
103 | 106 | ||
@@ -302,29 +305,36 @@ static int blkif_queue_request(struct request *req) | |||
302 | ring_req->operation = info->flush_op; | 305 | ring_req->operation = info->flush_op; |
303 | } | 306 | } |
304 | 307 | ||
305 | ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg); | 308 | if (unlikely(req->cmd_flags & REQ_DISCARD)) { |
306 | BUG_ON(ring_req->nr_segments > BLKIF_MAX_SEGMENTS_PER_REQUEST); | 309 | /* id, sector_number and handle are set above. */ |
310 | ring_req->operation = BLKIF_OP_DISCARD; | ||
311 | ring_req->nr_segments = 0; | ||
312 | ring_req->u.discard.nr_sectors = blk_rq_sectors(req); | ||
313 | } else { | ||
314 | ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg); | ||
315 | BUG_ON(ring_req->nr_segments > BLKIF_MAX_SEGMENTS_PER_REQUEST); | ||
307 | 316 | ||
308 | for_each_sg(info->sg, sg, ring_req->nr_segments, i) { | 317 | for_each_sg(info->sg, sg, ring_req->nr_segments, i) { |
309 | buffer_mfn = pfn_to_mfn(page_to_pfn(sg_page(sg))); | 318 | buffer_mfn = pfn_to_mfn(page_to_pfn(sg_page(sg))); |
310 | fsect = sg->offset >> 9; | 319 | fsect = sg->offset >> 9; |
311 | lsect = fsect + (sg->length >> 9) - 1; | 320 | lsect = fsect + (sg->length >> 9) - 1; |
312 | /* install a grant reference. */ | 321 | /* install a grant reference. */ |
313 | ref = gnttab_claim_grant_reference(&gref_head); | 322 | ref = gnttab_claim_grant_reference(&gref_head); |
314 | BUG_ON(ref == -ENOSPC); | 323 | BUG_ON(ref == -ENOSPC); |
315 | 324 | ||
316 | gnttab_grant_foreign_access_ref( | 325 | gnttab_grant_foreign_access_ref( |
317 | ref, | 326 | ref, |
318 | info->xbdev->otherend_id, | 327 | info->xbdev->otherend_id, |
319 | buffer_mfn, | 328 | buffer_mfn, |
320 | rq_data_dir(req) ); | 329 | rq_data_dir(req)); |
321 | 330 | ||
322 | info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn); | 331 | info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn); |
323 | ring_req->u.rw.seg[i] = | 332 | ring_req->u.rw.seg[i] = |
324 | (struct blkif_request_segment) { | 333 | (struct blkif_request_segment) { |
325 | .gref = ref, | 334 | .gref = ref, |
326 | .first_sect = fsect, | 335 | .first_sect = fsect, |
327 | .last_sect = lsect }; | 336 | .last_sect = lsect }; |
337 | } | ||
328 | } | 338 | } |
329 | 339 | ||
330 | info->ring.req_prod_pvt++; | 340 | info->ring.req_prod_pvt++; |
@@ -399,6 +409,7 @@ wait: | |||
399 | static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size) | 409 | static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size) |
400 | { | 410 | { |
401 | struct request_queue *rq; | 411 | struct request_queue *rq; |
412 | struct blkfront_info *info = gd->private_data; | ||
402 | 413 | ||
403 | rq = blk_init_queue(do_blkif_request, &blkif_io_lock); | 414 | rq = blk_init_queue(do_blkif_request, &blkif_io_lock); |
404 | if (rq == NULL) | 415 | if (rq == NULL) |
@@ -406,6 +417,13 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size) | |||
406 | 417 | ||
407 | queue_flag_set_unlocked(QUEUE_FLAG_VIRT, rq); | 418 | queue_flag_set_unlocked(QUEUE_FLAG_VIRT, rq); |
408 | 419 | ||
420 | if (info->feature_discard) { | ||
421 | queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, rq); | ||
422 | blk_queue_max_discard_sectors(rq, get_capacity(gd)); | ||
423 | rq->limits.discard_granularity = info->discard_granularity; | ||
424 | rq->limits.discard_alignment = info->discard_alignment; | ||
425 | } | ||
426 | |||
409 | /* Hard sector size and max sectors impersonate the equiv. hardware. */ | 427 | /* Hard sector size and max sectors impersonate the equiv. hardware. */ |
410 | blk_queue_logical_block_size(rq, sector_size); | 428 | blk_queue_logical_block_size(rq, sector_size); |
411 | blk_queue_max_hw_sectors(rq, 512); | 429 | blk_queue_max_hw_sectors(rq, 512); |
@@ -722,6 +740,19 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id) | |||
722 | 740 | ||
723 | error = (bret->status == BLKIF_RSP_OKAY) ? 0 : -EIO; | 741 | error = (bret->status == BLKIF_RSP_OKAY) ? 0 : -EIO; |
724 | switch (bret->operation) { | 742 | switch (bret->operation) { |
743 | case BLKIF_OP_DISCARD: | ||
744 | if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) { | ||
745 | struct request_queue *rq = info->rq; | ||
746 | printk(KERN_WARNING "blkfront: %s: discard op failed\n", | ||
747 | info->gd->disk_name); | ||
748 | error = -EOPNOTSUPP; | ||
749 | info->feature_discard = 0; | ||
750 | spin_lock(rq->queue_lock); | ||
751 | queue_flag_clear(QUEUE_FLAG_DISCARD, rq); | ||
752 | spin_unlock(rq->queue_lock); | ||
753 | } | ||
754 | __blk_end_request_all(req, error); | ||
755 | break; | ||
725 | case BLKIF_OP_FLUSH_DISKCACHE: | 756 | case BLKIF_OP_FLUSH_DISKCACHE: |
726 | case BLKIF_OP_WRITE_BARRIER: | 757 | case BLKIF_OP_WRITE_BARRIER: |
727 | if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) { | 758 | if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) { |
@@ -1098,6 +1129,33 @@ blkfront_closing(struct blkfront_info *info) | |||
1098 | bdput(bdev); | 1129 | bdput(bdev); |
1099 | } | 1130 | } |
1100 | 1131 | ||
1132 | static void blkfront_setup_discard(struct blkfront_info *info) | ||
1133 | { | ||
1134 | int err; | ||
1135 | char *type; | ||
1136 | unsigned int discard_granularity; | ||
1137 | unsigned int discard_alignment; | ||
1138 | |||
1139 | type = xenbus_read(XBT_NIL, info->xbdev->otherend, "type", NULL); | ||
1140 | if (IS_ERR(type)) | ||
1141 | return; | ||
1142 | |||
1143 | if (strncmp(type, "phy", 3) == 0) { | ||
1144 | err = xenbus_gather(XBT_NIL, info->xbdev->otherend, | ||
1145 | "discard-granularity", "%u", &discard_granularity, | ||
1146 | "discard-alignment", "%u", &discard_alignment, | ||
1147 | NULL); | ||
1148 | if (!err) { | ||
1149 | info->feature_discard = 1; | ||
1150 | info->discard_granularity = discard_granularity; | ||
1151 | info->discard_alignment = discard_alignment; | ||
1152 | } | ||
1153 | } else if (strncmp(type, "file", 4) == 0) | ||
1154 | info->feature_discard = 1; | ||
1155 | |||
1156 | kfree(type); | ||
1157 | } | ||
1158 | |||
1101 | /* | 1159 | /* |
1102 | * Invoked when the backend is finally 'ready' (and has told produced | 1160 | * Invoked when the backend is finally 'ready' (and has told produced |
1103 | * the details about the physical device - #sectors, size, etc). | 1161 | * the details about the physical device - #sectors, size, etc). |
@@ -1108,7 +1166,7 @@ static void blkfront_connect(struct blkfront_info *info) | |||
1108 | unsigned long sector_size; | 1166 | unsigned long sector_size; |
1109 | unsigned int binfo; | 1167 | unsigned int binfo; |
1110 | int err; | 1168 | int err; |
1111 | int barrier, flush; | 1169 | int barrier, flush, discard; |
1112 | 1170 | ||
1113 | switch (info->connected) { | 1171 | switch (info->connected) { |
1114 | case BLKIF_STATE_CONNECTED: | 1172 | case BLKIF_STATE_CONNECTED: |
@@ -1178,7 +1236,14 @@ static void blkfront_connect(struct blkfront_info *info) | |||
1178 | info->feature_flush = REQ_FLUSH; | 1236 | info->feature_flush = REQ_FLUSH; |
1179 | info->flush_op = BLKIF_OP_FLUSH_DISKCACHE; | 1237 | info->flush_op = BLKIF_OP_FLUSH_DISKCACHE; |
1180 | } | 1238 | } |
1181 | 1239 | ||
1240 | err = xenbus_gather(XBT_NIL, info->xbdev->otherend, | ||
1241 | "feature-discard", "%d", &discard, | ||
1242 | NULL); | ||
1243 | |||
1244 | if (!err && discard) | ||
1245 | blkfront_setup_discard(info); | ||
1246 | |||
1182 | err = xlvbd_alloc_gendisk(sectors, info, binfo, sector_size); | 1247 | err = xlvbd_alloc_gendisk(sectors, info, binfo, sector_size); |
1183 | if (err) { | 1248 | if (err) { |
1184 | xenbus_dev_fatal(info->xbdev, err, "xlvbd_add at %s", | 1249 | xenbus_dev_fatal(info->xbdev, err, "xlvbd_add at %s", |