diff options
author | Asias He <asias@redhat.com> | 2012-08-08 04:07:05 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2012-09-28 01:35:14 -0400 |
commit | c85a1f91b393a6c0c2ad382ba59d7618b29ab758 (patch) | |
tree | 6996b834bc514724c33553b5845e9532fcfa646c /drivers/block/virtio_blk.c | |
parent | a98755c559e0e944a44174883b74a97019e3a367 (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.c | 272 |
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 | ||
68 | enum { | ||
69 | VBLK_IS_FLUSH = 1, | ||
70 | VBLK_REQ_FLUSH = 2, | ||
71 | VBLK_REQ_DATA = 4, | ||
72 | VBLK_REQ_FUA = 8, | ||
73 | }; | ||
74 | |||
65 | static inline int virtblk_result(struct virtblk_req *vbr) | 75 | static 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 | ||
77 | static inline void virtblk_request_done(struct virtio_blk *vblk, | 87 | static 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 | |||
101 | static 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 | |||
128 | static 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 | |||
144 | static 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 | |||
160 | static 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 | |||
193 | static 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 | |||
202 | static 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 | |||
211 | static 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 | ||
95 | static inline void virtblk_bio_done(struct virtio_blk *vblk, | 229 | static 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 | |||
243 | static 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 | |||
258 | static 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 | ||
102 | static void virtblk_done(struct virtqueue *vq) | 266 | static 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 | ||
129 | static 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 | |||
141 | static bool do_req(struct request_queue *q, struct virtio_blk *vblk, | 293 | static 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 | ||
245 | static 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 | |||
272 | static void virtblk_make_request(struct request_queue *q, struct bio *bio) | 397 | static 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); |