diff options
Diffstat (limited to 'drivers/block/xen-blkfront.c')
-rw-r--r-- | drivers/block/xen-blkfront.c | 51 |
1 files changed, 37 insertions, 14 deletions
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 9cb8668ff5f4..b536a9cef917 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c | |||
@@ -97,6 +97,7 @@ struct blkfront_info | |||
97 | struct blk_shadow shadow[BLK_RING_SIZE]; | 97 | struct blk_shadow shadow[BLK_RING_SIZE]; |
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 | int is_ready; | 101 | int is_ready; |
101 | }; | 102 | }; |
102 | 103 | ||
@@ -250,8 +251,7 @@ static int blkif_ioctl(struct block_device *bdev, fmode_t mode, | |||
250 | 251 | ||
251 | /* | 252 | /* |
252 | * Generate a Xen blkfront IO request from a blk layer request. Reads | 253 | * Generate a Xen blkfront IO request from a blk layer request. Reads |
253 | * and writes are handled as expected. Since we lack a loose flush | 254 | * and writes are handled as expected. |
254 | * request, we map flushes into a full ordered barrier. | ||
255 | * | 255 | * |
256 | * @req: a request struct | 256 | * @req: a request struct |
257 | */ | 257 | */ |
@@ -293,14 +293,13 @@ static int blkif_queue_request(struct request *req) | |||
293 | 293 | ||
294 | if (req->cmd_flags & (REQ_FLUSH | REQ_FUA)) { | 294 | if (req->cmd_flags & (REQ_FLUSH | REQ_FUA)) { |
295 | /* | 295 | /* |
296 | * Ideally we could just do an unordered | 296 | * Ideally we can do an unordered flush-to-disk. In case the |
297 | * flush-to-disk, but all we have is a full write | 297 | * backend onlysupports barriers, use that. A barrier request |
298 | * barrier at the moment. However, a barrier write is | ||
299 | * a superset of FUA, so we can implement it the same | 298 | * a superset of FUA, so we can implement it the same |
300 | * way. (It's also a FLUSH+FUA, since it is | 299 | * way. (It's also a FLUSH+FUA, since it is |
301 | * guaranteed ordered WRT previous writes.) | 300 | * guaranteed ordered WRT previous writes.) |
302 | */ | 301 | */ |
303 | ring_req->operation = BLKIF_OP_WRITE_BARRIER; | 302 | ring_req->operation = info->flush_op; |
304 | } | 303 | } |
305 | 304 | ||
306 | ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg); | 305 | ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg); |
@@ -433,8 +432,11 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size) | |||
433 | static void xlvbd_flush(struct blkfront_info *info) | 432 | static void xlvbd_flush(struct blkfront_info *info) |
434 | { | 433 | { |
435 | blk_queue_flush(info->rq, info->feature_flush); | 434 | blk_queue_flush(info->rq, info->feature_flush); |
436 | printk(KERN_INFO "blkfront: %s: barriers %s\n", | 435 | printk(KERN_INFO "blkfront: %s: %s: %s\n", |
437 | info->gd->disk_name, | 436 | info->gd->disk_name, |
437 | info->flush_op == BLKIF_OP_WRITE_BARRIER ? | ||
438 | "barrier" : (info->flush_op == BLKIF_OP_FLUSH_DISKCACHE ? | ||
439 | "flush diskcache" : "barrier or flush"), | ||
438 | info->feature_flush ? "enabled" : "disabled"); | 440 | info->feature_flush ? "enabled" : "disabled"); |
439 | } | 441 | } |
440 | 442 | ||
@@ -720,15 +722,20 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id) | |||
720 | 722 | ||
721 | error = (bret->status == BLKIF_RSP_OKAY) ? 0 : -EIO; | 723 | error = (bret->status == BLKIF_RSP_OKAY) ? 0 : -EIO; |
722 | switch (bret->operation) { | 724 | switch (bret->operation) { |
725 | case BLKIF_OP_FLUSH_DISKCACHE: | ||
723 | case BLKIF_OP_WRITE_BARRIER: | 726 | case BLKIF_OP_WRITE_BARRIER: |
724 | if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) { | 727 | if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) { |
725 | printk(KERN_WARNING "blkfront: %s: write barrier op failed\n", | 728 | printk(KERN_WARNING "blkfront: %s: write %s op failed\n", |
729 | info->flush_op == BLKIF_OP_WRITE_BARRIER ? | ||
730 | "barrier" : "flush disk cache", | ||
726 | info->gd->disk_name); | 731 | info->gd->disk_name); |
727 | error = -EOPNOTSUPP; | 732 | error = -EOPNOTSUPP; |
728 | } | 733 | } |
729 | if (unlikely(bret->status == BLKIF_RSP_ERROR && | 734 | if (unlikely(bret->status == BLKIF_RSP_ERROR && |
730 | info->shadow[id].req.nr_segments == 0)) { | 735 | info->shadow[id].req.nr_segments == 0)) { |
731 | printk(KERN_WARNING "blkfront: %s: empty write barrier op failed\n", | 736 | printk(KERN_WARNING "blkfront: %s: empty write %s op failed\n", |
737 | info->flush_op == BLKIF_OP_WRITE_BARRIER ? | ||
738 | "barrier" : "flush disk cache", | ||
732 | info->gd->disk_name); | 739 | info->gd->disk_name); |
733 | error = -EOPNOTSUPP; | 740 | error = -EOPNOTSUPP; |
734 | } | 741 | } |
@@ -736,6 +743,7 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id) | |||
736 | if (error == -EOPNOTSUPP) | 743 | if (error == -EOPNOTSUPP) |
737 | error = 0; | 744 | error = 0; |
738 | info->feature_flush = 0; | 745 | info->feature_flush = 0; |
746 | info->flush_op = 0; | ||
739 | xlvbd_flush(info); | 747 | xlvbd_flush(info); |
740 | } | 748 | } |
741 | /* fall through */ | 749 | /* fall through */ |
@@ -1100,7 +1108,7 @@ static void blkfront_connect(struct blkfront_info *info) | |||
1100 | unsigned long sector_size; | 1108 | unsigned long sector_size; |
1101 | unsigned int binfo; | 1109 | unsigned int binfo; |
1102 | int err; | 1110 | int err; |
1103 | int barrier; | 1111 | int barrier, flush; |
1104 | 1112 | ||
1105 | switch (info->connected) { | 1113 | switch (info->connected) { |
1106 | case BLKIF_STATE_CONNECTED: | 1114 | case BLKIF_STATE_CONNECTED: |
@@ -1140,8 +1148,11 @@ static void blkfront_connect(struct blkfront_info *info) | |||
1140 | return; | 1148 | return; |
1141 | } | 1149 | } |
1142 | 1150 | ||
1151 | info->feature_flush = 0; | ||
1152 | info->flush_op = 0; | ||
1153 | |||
1143 | err = xenbus_gather(XBT_NIL, info->xbdev->otherend, | 1154 | err = xenbus_gather(XBT_NIL, info->xbdev->otherend, |
1144 | "feature-barrier", "%lu", &barrier, | 1155 | "feature-barrier", "%d", &barrier, |
1145 | NULL); | 1156 | NULL); |
1146 | 1157 | ||
1147 | /* | 1158 | /* |
@@ -1151,11 +1162,23 @@ static void blkfront_connect(struct blkfront_info *info) | |||
1151 | * | 1162 | * |
1152 | * If there are barriers, then we use flush. | 1163 | * If there are barriers, then we use flush. |
1153 | */ | 1164 | */ |
1154 | info->feature_flush = 0; | 1165 | if (!err && barrier) { |
1155 | |||
1156 | if (!err && barrier) | ||
1157 | info->feature_flush = REQ_FLUSH | REQ_FUA; | 1166 | info->feature_flush = REQ_FLUSH | REQ_FUA; |
1167 | info->flush_op = BLKIF_OP_WRITE_BARRIER; | ||
1168 | } | ||
1169 | /* | ||
1170 | * And if there is "feature-flush-cache" use that above | ||
1171 | * barriers. | ||
1172 | */ | ||
1173 | err = xenbus_gather(XBT_NIL, info->xbdev->otherend, | ||
1174 | "feature-flush-cache", "%d", &flush, | ||
1175 | NULL); | ||
1158 | 1176 | ||
1177 | if (!err && flush) { | ||
1178 | info->feature_flush = REQ_FLUSH; | ||
1179 | info->flush_op = BLKIF_OP_FLUSH_DISKCACHE; | ||
1180 | } | ||
1181 | |||
1159 | err = xlvbd_alloc_gendisk(sectors, info, binfo, sector_size); | 1182 | err = xlvbd_alloc_gendisk(sectors, info, binfo, sector_size); |
1160 | if (err) { | 1183 | if (err) { |
1161 | xenbus_dev_fatal(info->xbdev, err, "xlvbd_add at %s", | 1184 | xenbus_dev_fatal(info->xbdev, err, "xlvbd_add at %s", |