aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block/virtio_blk.c
diff options
context:
space:
mode:
authorAsias He <asias@redhat.com>2012-08-08 04:07:05 -0400
committerRusty Russell <rusty@rustcorp.com.au>2012-09-28 01:35:14 -0400
commitc85a1f91b393a6c0c2ad382ba59d7618b29ab758 (patch)
tree6996b834bc514724c33553b5845e9532fcfa646c /drivers/block/virtio_blk.c
parenta98755c559e0e944a44174883b74a97019e3a367 (diff)
virtio-blk: Add REQ_FLUSH and REQ_FUA support to bio path
We need to support both REQ_FLUSH and REQ_FUA for bio based path since it does not get the sequencing of REQ_FUA into REQ_FLUSH that request based drivers can request. REQ_FLUSH is emulated by: A) If the bio has no data to write: 1. Send VIRTIO_BLK_T_FLUSH to device, 2. In the flush I/O completion handler, finish the bio B) If the bio has data to write: 1. Send VIRTIO_BLK_T_FLUSH to device 2. In the flush I/O completion handler, send the actual write data to device 3. In the write I/O completion handler, finish the bio REQ_FUA is emulated by: 1. Send the actual write data to device 2. In the write I/O completion handler, send VIRTIO_BLK_T_FLUSH to device 3. In the flush I/O completion handler, finish the bio Changes in v7: - Using vbr->flags to trace request type - Dropped unnecessary struct virtio_blk *vblk parameter - Reuse struct virtblk_req in bio done function Cahnges in v6: - Reworked REQ_FLUSH and REQ_FUA emulatation order Cc: Rusty Russell <rusty@rustcorp.com.au> Cc: Jens Axboe <axboe@kernel.dk> Cc: Christoph Hellwig <hch@lst.de> Cc: Tejun Heo <tj@kernel.org> Cc: Shaohua Li <shli@kernel.org> Cc: "Michael S. Tsirkin" <mst@redhat.com> Cc: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: virtualization@lists.linux-foundation.org Signed-off-by: Asias He <asias@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/block/virtio_blk.c')
-rw-r--r--drivers/block/virtio_blk.c272
1 files changed, 188 insertions, 84 deletions
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 95cfeeda4f3a..2edfb5cef4f2 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -58,10 +58,20 @@ struct virtblk_req
58 struct bio *bio; 58 struct bio *bio;
59 struct virtio_blk_outhdr out_hdr; 59 struct virtio_blk_outhdr out_hdr;
60 struct virtio_scsi_inhdr in_hdr; 60 struct virtio_scsi_inhdr in_hdr;
61 struct work_struct work;
62 struct virtio_blk *vblk;
63 int flags;
61 u8 status; 64 u8 status;
62 struct scatterlist sg[]; 65 struct scatterlist sg[];
63}; 66};
64 67
68enum {
69 VBLK_IS_FLUSH = 1,
70 VBLK_REQ_FLUSH = 2,
71 VBLK_REQ_DATA = 4,
72 VBLK_REQ_FUA = 8,
73};
74
65static inline int virtblk_result(struct virtblk_req *vbr) 75static inline int virtblk_result(struct virtblk_req *vbr)
66{ 76{
67 switch (vbr->status) { 77 switch (vbr->status) {
@@ -74,9 +84,133 @@ static inline int virtblk_result(struct virtblk_req *vbr)
74 } 84 }
75} 85}
76 86
77static inline void virtblk_request_done(struct virtio_blk *vblk, 87static inline struct virtblk_req *virtblk_alloc_req(struct virtio_blk *vblk,
78 struct virtblk_req *vbr) 88 gfp_t gfp_mask)
89{
90 struct virtblk_req *vbr;
91
92 vbr = mempool_alloc(vblk->pool, gfp_mask);
93 if (vbr && use_bio)
94 sg_init_table(vbr->sg, vblk->sg_elems);
95
96 vbr->vblk = vblk;
97
98 return vbr;
99}
100
101static void virtblk_add_buf_wait(struct virtio_blk *vblk,
102 struct virtblk_req *vbr,
103 unsigned long out,
104 unsigned long in)
105{
106 DEFINE_WAIT(wait);
107
108 for (;;) {
109 prepare_to_wait_exclusive(&vblk->queue_wait, &wait,
110 TASK_UNINTERRUPTIBLE);
111
112 spin_lock_irq(vblk->disk->queue->queue_lock);
113 if (virtqueue_add_buf(vblk->vq, vbr->sg, out, in, vbr,
114 GFP_ATOMIC) < 0) {
115 spin_unlock_irq(vblk->disk->queue->queue_lock);
116 io_schedule();
117 } else {
118 virtqueue_kick(vblk->vq);
119 spin_unlock_irq(vblk->disk->queue->queue_lock);
120 break;
121 }
122
123 }
124
125 finish_wait(&vblk->queue_wait, &wait);
126}
127
128static inline void virtblk_add_req(struct virtblk_req *vbr,
129 unsigned int out, unsigned int in)
130{
131 struct virtio_blk *vblk = vbr->vblk;
132
133 spin_lock_irq(vblk->disk->queue->queue_lock);
134 if (unlikely(virtqueue_add_buf(vblk->vq, vbr->sg, out, in, vbr,
135 GFP_ATOMIC) < 0)) {
136 spin_unlock_irq(vblk->disk->queue->queue_lock);
137 virtblk_add_buf_wait(vblk, vbr, out, in);
138 return;
139 }
140 virtqueue_kick(vblk->vq);
141 spin_unlock_irq(vblk->disk->queue->queue_lock);
142}
143
144static int virtblk_bio_send_flush(struct virtblk_req *vbr)
145{
146 unsigned int out = 0, in = 0;
147
148 vbr->flags |= VBLK_IS_FLUSH;
149 vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH;
150 vbr->out_hdr.sector = 0;
151 vbr->out_hdr.ioprio = 0;
152 sg_set_buf(&vbr->sg[out++], &vbr->out_hdr, sizeof(vbr->out_hdr));
153 sg_set_buf(&vbr->sg[out + in++], &vbr->status, sizeof(vbr->status));
154
155 virtblk_add_req(vbr, out, in);
156
157 return 0;
158}
159
160static int virtblk_bio_send_data(struct virtblk_req *vbr)
79{ 161{
162 struct virtio_blk *vblk = vbr->vblk;
163 unsigned int num, out = 0, in = 0;
164 struct bio *bio = vbr->bio;
165
166 vbr->flags &= ~VBLK_IS_FLUSH;
167 vbr->out_hdr.type = 0;
168 vbr->out_hdr.sector = bio->bi_sector;
169 vbr->out_hdr.ioprio = bio_prio(bio);
170
171 sg_set_buf(&vbr->sg[out++], &vbr->out_hdr, sizeof(vbr->out_hdr));
172
173 num = blk_bio_map_sg(vblk->disk->queue, bio, vbr->sg + out);
174
175 sg_set_buf(&vbr->sg[num + out + in++], &vbr->status,
176 sizeof(vbr->status));
177
178 if (num) {
179 if (bio->bi_rw & REQ_WRITE) {
180 vbr->out_hdr.type |= VIRTIO_BLK_T_OUT;
181 out += num;
182 } else {
183 vbr->out_hdr.type |= VIRTIO_BLK_T_IN;
184 in += num;
185 }
186 }
187
188 virtblk_add_req(vbr, out, in);
189
190 return 0;
191}
192
193static void virtblk_bio_send_data_work(struct work_struct *work)
194{
195 struct virtblk_req *vbr;
196
197 vbr = container_of(work, struct virtblk_req, work);
198
199 virtblk_bio_send_data(vbr);
200}
201
202static void virtblk_bio_send_flush_work(struct work_struct *work)
203{
204 struct virtblk_req *vbr;
205
206 vbr = container_of(work, struct virtblk_req, work);
207
208 virtblk_bio_send_flush(vbr);
209}
210
211static inline void virtblk_request_done(struct virtblk_req *vbr)
212{
213 struct virtio_blk *vblk = vbr->vblk;
80 struct request *req = vbr->req; 214 struct request *req = vbr->req;
81 int error = virtblk_result(vbr); 215 int error = virtblk_result(vbr);
82 216
@@ -92,17 +226,47 @@ static inline void virtblk_request_done(struct virtio_blk *vblk,
92 mempool_free(vbr, vblk->pool); 226 mempool_free(vbr, vblk->pool);
93} 227}
94 228
95static inline void virtblk_bio_done(struct virtio_blk *vblk, 229static inline void virtblk_bio_flush_done(struct virtblk_req *vbr)
96 struct virtblk_req *vbr)
97{ 230{
98 bio_endio(vbr->bio, virtblk_result(vbr)); 231 struct virtio_blk *vblk = vbr->vblk;
99 mempool_free(vbr, vblk->pool); 232
233 if (vbr->flags & VBLK_REQ_DATA) {
234 /* Send out the actual write data */
235 INIT_WORK(&vbr->work, virtblk_bio_send_data_work);
236 queue_work(virtblk_wq, &vbr->work);
237 } else {
238 bio_endio(vbr->bio, virtblk_result(vbr));
239 mempool_free(vbr, vblk->pool);
240 }
241}
242
243static inline void virtblk_bio_data_done(struct virtblk_req *vbr)
244{
245 struct virtio_blk *vblk = vbr->vblk;
246
247 if (unlikely(vbr->flags & VBLK_REQ_FUA)) {
248 /* Send out a flush before end the bio */
249 vbr->flags &= ~VBLK_REQ_DATA;
250 INIT_WORK(&vbr->work, virtblk_bio_send_flush_work);
251 queue_work(virtblk_wq, &vbr->work);
252 } else {
253 bio_endio(vbr->bio, virtblk_result(vbr));
254 mempool_free(vbr, vblk->pool);
255 }
256}
257
258static inline void virtblk_bio_done(struct virtblk_req *vbr)
259{
260 if (unlikely(vbr->flags & VBLK_IS_FLUSH))
261 virtblk_bio_flush_done(vbr);
262 else
263 virtblk_bio_data_done(vbr);
100} 264}
101 265
102static void virtblk_done(struct virtqueue *vq) 266static void virtblk_done(struct virtqueue *vq)
103{ 267{
104 struct virtio_blk *vblk = vq->vdev->priv; 268 struct virtio_blk *vblk = vq->vdev->priv;
105 unsigned long bio_done = 0, req_done = 0; 269 bool bio_done = false, req_done = false;
106 struct virtblk_req *vbr; 270 struct virtblk_req *vbr;
107 unsigned long flags; 271 unsigned long flags;
108 unsigned int len; 272 unsigned int len;
@@ -110,11 +274,11 @@ static void virtblk_done(struct virtqueue *vq)
110 spin_lock_irqsave(vblk->disk->queue->queue_lock, flags); 274 spin_lock_irqsave(vblk->disk->queue->queue_lock, flags);
111 while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) { 275 while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) {
112 if (vbr->bio) { 276 if (vbr->bio) {
113 virtblk_bio_done(vblk, vbr); 277 virtblk_bio_done(vbr);
114 bio_done++; 278 bio_done = true;
115 } else { 279 } else {
116 virtblk_request_done(vblk, vbr); 280 virtblk_request_done(vbr);
117 req_done++; 281 req_done = true;
118 } 282 }
119 } 283 }
120 /* In case queue is stopped waiting for more buffers. */ 284 /* In case queue is stopped waiting for more buffers. */
@@ -126,18 +290,6 @@ static void virtblk_done(struct virtqueue *vq)
126 wake_up(&vblk->queue_wait); 290 wake_up(&vblk->queue_wait);
127} 291}
128 292
129static inline struct virtblk_req *virtblk_alloc_req(struct virtio_blk *vblk,
130 gfp_t gfp_mask)
131{
132 struct virtblk_req *vbr;
133
134 vbr = mempool_alloc(vblk->pool, gfp_mask);
135 if (vbr && use_bio)
136 sg_init_table(vbr->sg, vblk->sg_elems);
137
138 return vbr;
139}
140
141static bool do_req(struct request_queue *q, struct virtio_blk *vblk, 293static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
142 struct request *req) 294 struct request *req)
143{ 295{
@@ -242,41 +394,12 @@ static void virtblk_request(struct request_queue *q)
242 virtqueue_kick(vblk->vq); 394 virtqueue_kick(vblk->vq);
243} 395}
244 396
245static void virtblk_add_buf_wait(struct virtio_blk *vblk,
246 struct virtblk_req *vbr,
247 unsigned long out,
248 unsigned long in)
249{
250 DEFINE_WAIT(wait);
251
252 for (;;) {
253 prepare_to_wait_exclusive(&vblk->queue_wait, &wait,
254 TASK_UNINTERRUPTIBLE);
255
256 spin_lock_irq(vblk->disk->queue->queue_lock);
257 if (virtqueue_add_buf(vblk->vq, vbr->sg, out, in, vbr,
258 GFP_ATOMIC) < 0) {
259 spin_unlock_irq(vblk->disk->queue->queue_lock);
260 io_schedule();
261 } else {
262 virtqueue_kick(vblk->vq);
263 spin_unlock_irq(vblk->disk->queue->queue_lock);
264 break;
265 }
266
267 }
268
269 finish_wait(&vblk->queue_wait, &wait);
270}
271
272static void virtblk_make_request(struct request_queue *q, struct bio *bio) 397static void virtblk_make_request(struct request_queue *q, struct bio *bio)
273{ 398{
274 struct virtio_blk *vblk = q->queuedata; 399 struct virtio_blk *vblk = q->queuedata;
275 unsigned int num, out = 0, in = 0;
276 struct virtblk_req *vbr; 400 struct virtblk_req *vbr;
277 401
278 BUG_ON(bio->bi_phys_segments + 2 > vblk->sg_elems); 402 BUG_ON(bio->bi_phys_segments + 2 > vblk->sg_elems);
279 BUG_ON(bio->bi_rw & (REQ_FLUSH | REQ_FUA));
280 403
281 vbr = virtblk_alloc_req(vblk, GFP_NOIO); 404 vbr = virtblk_alloc_req(vblk, GFP_NOIO);
282 if (!vbr) { 405 if (!vbr) {
@@ -285,37 +408,18 @@ static void virtblk_make_request(struct request_queue *q, struct bio *bio)
285 } 408 }
286 409
287 vbr->bio = bio; 410 vbr->bio = bio;
288 vbr->req = NULL; 411 vbr->flags = 0;
289 vbr->out_hdr.type = 0; 412 if (bio->bi_rw & REQ_FLUSH)
290 vbr->out_hdr.sector = bio->bi_sector; 413 vbr->flags |= VBLK_REQ_FLUSH;
291 vbr->out_hdr.ioprio = bio_prio(bio); 414 if (bio->bi_rw & REQ_FUA)
292 415 vbr->flags |= VBLK_REQ_FUA;
293 sg_set_buf(&vbr->sg[out++], &vbr->out_hdr, sizeof(vbr->out_hdr)); 416 if (bio->bi_size)
294 417 vbr->flags |= VBLK_REQ_DATA;
295 num = blk_bio_map_sg(q, bio, vbr->sg + out); 418
296 419 if (unlikely(vbr->flags & VBLK_REQ_FLUSH))
297 sg_set_buf(&vbr->sg[num + out + in++], &vbr->status, 420 virtblk_bio_send_flush(vbr);
298 sizeof(vbr->status)); 421 else
299 422 virtblk_bio_send_data(vbr);
300 if (num) {
301 if (bio->bi_rw & REQ_WRITE) {
302 vbr->out_hdr.type |= VIRTIO_BLK_T_OUT;
303 out += num;
304 } else {
305 vbr->out_hdr.type |= VIRTIO_BLK_T_IN;
306 in += num;
307 }
308 }
309
310 spin_lock_irq(vblk->disk->queue->queue_lock);
311 if (unlikely(virtqueue_add_buf(vblk->vq, vbr->sg, out, in, vbr,
312 GFP_ATOMIC) < 0)) {
313 spin_unlock_irq(vblk->disk->queue->queue_lock);
314 virtblk_add_buf_wait(vblk, vbr, out, in);
315 return;
316 }
317 virtqueue_kick(vblk->vq);
318 spin_unlock_irq(vblk->disk->queue->queue_lock);
319} 423}
320 424
321/* return id (s/n) string for *disk to *id_str 425/* return id (s/n) string for *disk to *id_str
@@ -529,7 +633,7 @@ static void virtblk_update_cache_mode(struct virtio_device *vdev)
529 u8 writeback = virtblk_get_cache_mode(vdev); 633 u8 writeback = virtblk_get_cache_mode(vdev);
530 struct virtio_blk *vblk = vdev->priv; 634 struct virtio_blk *vblk = vdev->priv;
531 635
532 if (writeback && !use_bio) 636 if (writeback)
533 blk_queue_flush(vblk->disk->queue, REQ_FLUSH); 637 blk_queue_flush(vblk->disk->queue, REQ_FLUSH);
534 else 638 else
535 blk_queue_flush(vblk->disk->queue, 0); 639 blk_queue_flush(vblk->disk->queue, 0);