Commit | Line | Data |
---|---|---|
f82bd046 | 1 | /* |
f82bd046 HJ |
2 | * Copyright (c) 2009, Microsoft Corporation. |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms and conditions of the GNU General Public License, | |
6 | * version 2, as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope it will be useful, but WITHOUT | |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
11 | * more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License along with | |
14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | |
15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | |
16 | * | |
17 | * Authors: | |
d0e94d17 | 18 | * Haiyang Zhang <haiyangz@microsoft.com> |
f82bd046 | 19 | * Hank Janssen <hjanssen@microsoft.com> |
e757046f | 20 | * K. Y. Srinivasan <kys@microsoft.com> |
f82bd046 | 21 | */ |
f82bd046 HJ |
22 | #include <linux/init.h> |
23 | #include <linux/module.h> | |
24 | #include <linux/device.h> | |
25 | #include <linux/blkdev.h> | |
26 | #include <linux/major.h> | |
27 | #include <linux/delay.h> | |
28 | #include <linux/hdreg.h> | |
5a0e3ad6 | 29 | #include <linux/slab.h> |
f82bd046 HJ |
30 | #include <scsi/scsi.h> |
31 | #include <scsi/scsi_cmnd.h> | |
32 | #include <scsi/scsi_eh.h> | |
33 | #include <scsi/scsi_dbg.h> | |
3f335ea2 S |
34 | |
35 | #include "hyperv.h" | |
cdee1504 | 36 | #include "hyperv_storage.h" |
f82bd046 | 37 | |
454f18a9 | 38 | |
f82bd046 HJ |
39 | #define BLKVSC_MINORS 64 |
40 | ||
f82bd046 HJ |
41 | enum blkvsc_device_type { |
42 | UNKNOWN_DEV_TYPE, | |
43 | HARDDISK_TYPE, | |
44 | DVD_TYPE, | |
45 | }; | |
46 | ||
2ede209f S |
47 | enum blkvsc_op_type { |
48 | DO_INQUIRY, | |
2038e214 | 49 | DO_CAPACITY, |
26a19739 | 50 | DO_FLUSH, |
2ede209f S |
51 | }; |
52 | ||
454f18a9 BP |
53 | /* |
54 | * This request ties the struct request and struct | |
0b3f6834 | 55 | * blkvsc_request/hv_storvsc_request together A struct request may be |
454f18a9 BP |
56 | * represented by 1 or more struct blkvsc_request |
57 | */ | |
f82bd046 | 58 | struct blkvsc_request_group { |
8a280399 GKH |
59 | int outstanding; |
60 | int status; | |
61 | struct list_head blkvsc_req_list; /* list of blkvsc_requests */ | |
f82bd046 HJ |
62 | }; |
63 | ||
f82bd046 | 64 | struct blkvsc_request { |
8a280399 GKH |
65 | /* blkvsc_request_group.blkvsc_req_list */ |
66 | struct list_head req_entry; | |
67 | ||
68 | /* block_device_context.pending_list */ | |
69 | struct list_head pend_entry; | |
70 | ||
71 | /* This may be null if we generate a request internally */ | |
72 | struct request *req; | |
f82bd046 | 73 | |
8a280399 | 74 | struct block_device_context *dev; |
f82bd046 | 75 | |
8a280399 GKH |
76 | /* The group this request is part of. Maybe null */ |
77 | struct blkvsc_request_group *group; | |
f82bd046 | 78 | |
8a280399 GKH |
79 | int write; |
80 | sector_t sector_start; | |
81 | unsigned long sector_count; | |
f82bd046 HJ |
82 | |
83 | unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; | |
84 | unsigned char cmd_len; | |
85 | unsigned char cmnd[MAX_COMMAND_SIZE]; | |
86 | ||
0b3f6834 | 87 | struct hv_storvsc_request request; |
f82bd046 HJ |
88 | }; |
89 | ||
454f18a9 | 90 | /* Per device structure */ |
f82bd046 | 91 | struct block_device_context { |
8a280399 | 92 | /* point back to our device context */ |
6bad88da | 93 | struct hv_device *device_ctx; |
8a280399 GKH |
94 | struct kmem_cache *request_pool; |
95 | spinlock_t lock; | |
96 | struct gendisk *gd; | |
f82bd046 | 97 | enum blkvsc_device_type device_type; |
8a280399 GKH |
98 | struct list_head pending_list; |
99 | ||
100 | unsigned char device_id[64]; | |
101 | unsigned int device_id_len; | |
102 | int num_outstanding_reqs; | |
103 | int shutting_down; | |
8a280399 GKH |
104 | unsigned int sector_size; |
105 | sector_t capacity; | |
106 | unsigned int port; | |
107 | unsigned char path; | |
108 | unsigned char target; | |
109 | int users; | |
f82bd046 HJ |
110 | }; |
111 | ||
19b5931d | 112 | |
8f8e57ab S |
113 | /* |
114 | * There is a circular dependency involving blkvsc_request_completion() | |
115 | * and blkvsc_do_request(). | |
116 | */ | |
117 | static void blkvsc_request_completion(struct hv_storvsc_request *request); | |
118 | ||
f84044d3 S |
119 | static int blkvsc_ringbuffer_size = BLKVSC_RING_BUFFER_SIZE; |
120 | ||
369cb5ce S |
121 | module_param(blkvsc_ringbuffer_size, int, S_IRUGO); |
122 | MODULE_PARM_DESC(ring_size, "Ring buffer size (in bytes)"); | |
123 | ||
f84044d3 S |
124 | /* |
125 | * There is a circular dependency involving blkvsc_probe() | |
126 | * and block_ops. | |
127 | */ | |
9efd21e1 | 128 | static int blkvsc_probe(struct hv_device *dev); |
f84044d3 | 129 | |
34d620da | 130 | static int blkvsc_device_add(struct hv_device *device, |
19b5931d S |
131 | void *additional_info) |
132 | { | |
133 | struct storvsc_device_info *device_info; | |
134 | int ret = 0; | |
135 | ||
136 | device_info = (struct storvsc_device_info *)additional_info; | |
137 | ||
fa4d123a S |
138 | device_info->ring_buffer_size = blkvsc_ringbuffer_size; |
139 | ||
2ac5dad1 | 140 | ret = storvsc_dev_add(device, additional_info); |
19b5931d S |
141 | if (ret != 0) |
142 | return ret; | |
143 | ||
144 | /* | |
145 | * We need to use the device instance guid to set the path and target | |
146 | * id. For IDE devices, the device instance id is formatted as | |
147 | * <bus id> * - <device id> - 8899 - 000000000000. | |
148 | */ | |
358d2ee2 S |
149 | device_info->path_id = device->dev_instance.b[3] << 24 | |
150 | device->dev_instance.b[2] << 16 | | |
151 | device->dev_instance.b[1] << 8 | | |
152 | device->dev_instance.b[0]; | |
19b5931d | 153 | |
358d2ee2 S |
154 | device_info->target_id = device->dev_instance.b[5] << 8 | |
155 | device->dev_instance.b[4]; | |
19b5931d S |
156 | |
157 | return ret; | |
158 | } | |
159 | ||
8cbe3a1c S |
160 | static int blkvsc_submit_request(struct blkvsc_request *blkvsc_req, |
161 | void (*request_completion)(struct hv_storvsc_request *)) | |
162 | { | |
163 | struct block_device_context *blkdev = blkvsc_req->dev; | |
8cbe3a1c S |
164 | struct hv_storvsc_request *storvsc_req; |
165 | struct vmscsi_request *vm_srb; | |
166 | int ret; | |
167 | ||
8cbe3a1c S |
168 | |
169 | storvsc_req = &blkvsc_req->request; | |
170 | vm_srb = &storvsc_req->vstor_packet.vm_srb; | |
171 | ||
172 | vm_srb->data_in = blkvsc_req->write ? WRITE_TYPE : READ_TYPE; | |
173 | ||
174 | storvsc_req->on_io_completion = request_completion; | |
175 | storvsc_req->context = blkvsc_req; | |
176 | ||
177 | vm_srb->port_number = blkdev->port; | |
178 | vm_srb->path_id = blkdev->path; | |
179 | vm_srb->target_id = blkdev->target; | |
180 | vm_srb->lun = 0; /* this is not really used at all */ | |
181 | ||
182 | vm_srb->cdb_length = blkvsc_req->cmd_len; | |
183 | ||
184 | memcpy(vm_srb->cdb, blkvsc_req->cmnd, vm_srb->cdb_length); | |
185 | ||
186 | storvsc_req->sense_buffer = blkvsc_req->sense_buffer; | |
187 | ||
7b317799 | 188 | ret = storvsc_do_io(blkdev->device_ctx, |
8cbe3a1c S |
189 | &blkvsc_req->request); |
190 | if (ret == 0) | |
191 | blkdev->num_outstanding_reqs++; | |
192 | ||
193 | return ret; | |
194 | } | |
195 | ||
196 | ||
a55af7be S |
197 | static int blkvsc_open(struct block_device *bdev, fmode_t mode) |
198 | { | |
199 | struct block_device_context *blkdev = bdev->bd_disk->private_data; | |
6b73e4c0 | 200 | unsigned long flags; |
a55af7be | 201 | |
6b73e4c0 | 202 | spin_lock_irqsave(&blkdev->lock, flags); |
a55af7be | 203 | |
a55af7be S |
204 | blkdev->users++; |
205 | ||
6b73e4c0 | 206 | spin_unlock_irqrestore(&blkdev->lock, flags); |
c4c58c58 | 207 | |
a55af7be S |
208 | return 0; |
209 | } | |
210 | ||
273083be S |
211 | |
212 | static int blkvsc_getgeo(struct block_device *bd, struct hd_geometry *hg) | |
213 | { | |
ea50245f S |
214 | sector_t nsect = get_capacity(bd->bd_disk); |
215 | sector_t cylinders = nsect; | |
273083be | 216 | |
ea50245f S |
217 | /* |
218 | * We are making up these values; let us keep it simple. | |
219 | */ | |
220 | hg->heads = 0xff; | |
221 | hg->sectors = 0x3f; | |
222 | sector_div(cylinders, hg->heads * hg->sectors); | |
273083be | 223 | hg->cylinders = cylinders; |
ea50245f S |
224 | if ((sector_t)(hg->cylinders + 1) * hg->heads * hg->sectors < nsect) |
225 | hg->cylinders = 0xffff; | |
273083be | 226 | return 0; |
ea50245f | 227 | |
273083be S |
228 | } |
229 | ||
b7be09cc S |
230 | |
231 | static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req) | |
232 | { | |
b7be09cc S |
233 | |
234 | blkvsc_req->cmd_len = 16; | |
235 | ||
9bd0859a S |
236 | if (rq_data_dir(blkvsc_req->req)) { |
237 | blkvsc_req->write = 1; | |
238 | blkvsc_req->cmnd[0] = WRITE_16; | |
b7be09cc | 239 | } else { |
9bd0859a S |
240 | blkvsc_req->write = 0; |
241 | blkvsc_req->cmnd[0] = READ_16; | |
b7be09cc | 242 | } |
9bd0859a S |
243 | |
244 | blkvsc_req->cmnd[1] |= | |
245 | (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0; | |
246 | ||
247 | *(unsigned long long *)&blkvsc_req->cmnd[2] = | |
248 | cpu_to_be64(blkvsc_req->sector_start); | |
249 | *(unsigned int *)&blkvsc_req->cmnd[10] = | |
250 | cpu_to_be32(blkvsc_req->sector_count); | |
b7be09cc S |
251 | } |
252 | ||
253 | ||
7955344f | 254 | static int blkvsc_ioctl(struct block_device *bd, fmode_t mode, |
7b04cd08 | 255 | unsigned cmd, unsigned long arg) |
7955344f | 256 | { |
7b04cd08 S |
257 | struct block_device_context *blkdev = bd->bd_disk->private_data; |
258 | int ret = 0; | |
7955344f S |
259 | |
260 | switch (cmd) { | |
7955344f | 261 | case HDIO_GET_IDENTITY: |
7955344f S |
262 | if (copy_to_user((void __user *)arg, blkdev->device_id, |
263 | blkdev->device_id_len)) | |
264 | ret = -EFAULT; | |
265 | break; | |
7955344f S |
266 | default: |
267 | ret = -EINVAL; | |
268 | break; | |
269 | } | |
270 | ||
271 | return ret; | |
272 | } | |
273 | ||
795d30bb S |
274 | static void blkvsc_cmd_completion(struct hv_storvsc_request *request) |
275 | { | |
276 | struct blkvsc_request *blkvsc_req = | |
277 | (struct blkvsc_request *)request->context; | |
278 | struct block_device_context *blkdev = | |
279 | (struct block_device_context *)blkvsc_req->dev; | |
280 | struct scsi_sense_hdr sense_hdr; | |
281 | struct vmscsi_request *vm_srb; | |
67de49c1 | 282 | unsigned long flags; |
795d30bb | 283 | |
795d30bb S |
284 | |
285 | vm_srb = &blkvsc_req->request.vstor_packet.vm_srb; | |
67de49c1 S |
286 | |
287 | spin_lock_irqsave(&blkdev->lock, flags); | |
795d30bb | 288 | blkdev->num_outstanding_reqs--; |
67de49c1 | 289 | spin_unlock_irqrestore(&blkdev->lock, flags); |
795d30bb S |
290 | |
291 | if (vm_srb->scsi_status) | |
292 | if (scsi_normalize_sense(blkvsc_req->sense_buffer, | |
293 | SCSI_SENSE_BUFFERSIZE, &sense_hdr)) | |
294 | scsi_print_sense_hdr("blkvsc", &sense_hdr); | |
295 | ||
296 | complete(&blkvsc_req->request.wait_event); | |
297 | } | |
298 | ||
2ede209f S |
299 | |
300 | static int blkvsc_do_operation(struct block_device_context *blkdev, | |
301 | enum blkvsc_op_type op) | |
302 | { | |
303 | struct blkvsc_request *blkvsc_req; | |
304 | struct page *page_buf; | |
305 | unsigned char *buf; | |
306 | unsigned char device_type; | |
2038e214 S |
307 | struct scsi_sense_hdr sense_hdr; |
308 | struct vmscsi_request *vm_srb; | |
67de49c1 | 309 | unsigned long flags; |
2038e214 | 310 | |
2ede209f S |
311 | int ret = 0; |
312 | ||
313 | blkvsc_req = kmem_cache_zalloc(blkdev->request_pool, GFP_KERNEL); | |
314 | if (!blkvsc_req) | |
315 | return -ENOMEM; | |
316 | ||
317 | page_buf = alloc_page(GFP_KERNEL); | |
318 | if (!page_buf) { | |
fe3e5936 | 319 | kmem_cache_free(blkdev->request_pool, blkvsc_req); |
2ede209f S |
320 | return -ENOMEM; |
321 | } | |
322 | ||
2038e214 | 323 | vm_srb = &blkvsc_req->request.vstor_packet.vm_srb; |
2ede209f S |
324 | init_completion(&blkvsc_req->request.wait_event); |
325 | blkvsc_req->dev = blkdev; | |
326 | blkvsc_req->req = NULL; | |
327 | blkvsc_req->write = 0; | |
328 | ||
329 | blkvsc_req->request.data_buffer.pfn_array[0] = | |
330 | page_to_pfn(page_buf); | |
331 | blkvsc_req->request.data_buffer.offset = 0; | |
332 | ||
333 | switch (op) { | |
334 | case DO_INQUIRY: | |
335 | blkvsc_req->cmnd[0] = INQUIRY; | |
336 | blkvsc_req->cmnd[1] = 0x1; /* Get product data */ | |
337 | blkvsc_req->cmnd[2] = 0x83; /* mode page 83 */ | |
338 | blkvsc_req->cmnd[4] = 64; | |
339 | blkvsc_req->cmd_len = 6; | |
340 | blkvsc_req->request.data_buffer.len = 64; | |
341 | break; | |
342 | ||
2038e214 S |
343 | case DO_CAPACITY: |
344 | blkdev->sector_size = 0; | |
345 | blkdev->capacity = 0; | |
2038e214 S |
346 | |
347 | blkvsc_req->cmnd[0] = READ_CAPACITY; | |
348 | blkvsc_req->cmd_len = 16; | |
349 | blkvsc_req->request.data_buffer.len = 8; | |
350 | break; | |
26a19739 S |
351 | |
352 | case DO_FLUSH: | |
353 | blkvsc_req->cmnd[0] = SYNCHRONIZE_CACHE; | |
354 | blkvsc_req->cmd_len = 10; | |
355 | blkvsc_req->request.data_buffer.pfn_array[0] = 0; | |
356 | blkvsc_req->request.data_buffer.len = 0; | |
357 | break; | |
2ede209f S |
358 | default: |
359 | ret = -EINVAL; | |
360 | goto cleanup; | |
361 | } | |
362 | ||
67de49c1 | 363 | spin_lock_irqsave(&blkdev->lock, flags); |
2ede209f | 364 | blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); |
67de49c1 | 365 | spin_unlock_irqrestore(&blkdev->lock, flags); |
2ede209f S |
366 | |
367 | wait_for_completion_interruptible(&blkvsc_req->request.wait_event); | |
368 | ||
2038e214 S |
369 | /* check error */ |
370 | if (vm_srb->scsi_status) { | |
371 | scsi_normalize_sense(blkvsc_req->sense_buffer, | |
372 | SCSI_SENSE_BUFFERSIZE, &sense_hdr); | |
373 | ||
2038e214 S |
374 | return 0; |
375 | } | |
376 | ||
2ede209f S |
377 | buf = kmap(page_buf); |
378 | ||
379 | switch (op) { | |
380 | case DO_INQUIRY: | |
381 | device_type = buf[0] & 0x1F; | |
382 | ||
383 | if (device_type == 0x0) | |
384 | blkdev->device_type = HARDDISK_TYPE; | |
2ede209f S |
385 | else |
386 | blkdev->device_type = UNKNOWN_DEV_TYPE; | |
387 | ||
388 | blkdev->device_id_len = buf[7]; | |
389 | if (blkdev->device_id_len > 64) | |
390 | blkdev->device_id_len = 64; | |
391 | ||
392 | memcpy(blkdev->device_id, &buf[8], blkdev->device_id_len); | |
393 | break; | |
2038e214 S |
394 | |
395 | case DO_CAPACITY: | |
396 | /* be to le */ | |
397 | blkdev->capacity = | |
398 | ((buf[0] << 24) | (buf[1] << 16) | | |
399 | (buf[2] << 8) | buf[3]) + 1; | |
400 | ||
401 | blkdev->sector_size = | |
402 | (buf[4] << 24) | (buf[5] << 16) | | |
403 | (buf[6] << 8) | buf[7]; | |
404 | break; | |
26a19739 S |
405 | default: |
406 | break; | |
407 | ||
2ede209f S |
408 | } |
409 | ||
410 | cleanup: | |
411 | ||
412 | kunmap(page_buf); | |
413 | ||
414 | __free_page(page_buf); | |
415 | ||
fe3e5936 | 416 | kmem_cache_free(blkdev->request_pool, blkvsc_req); |
2ede209f S |
417 | |
418 | return ret; | |
419 | } | |
420 | ||
795d30bb | 421 | |
f60f2138 S |
422 | static int blkvsc_cancel_pending_reqs(struct block_device_context *blkdev) |
423 | { | |
424 | struct blkvsc_request *pend_req, *tmp; | |
425 | struct blkvsc_request *comp_req, *tmp2; | |
426 | struct vmscsi_request *vm_srb; | |
427 | ||
428 | int ret = 0; | |
429 | ||
f60f2138 S |
430 | |
431 | /* Flush the pending list first */ | |
432 | list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list, | |
433 | pend_entry) { | |
434 | /* | |
435 | * The pend_req could be part of a partially completed | |
436 | * request. If so, complete those req first until we | |
437 | * hit the pend_req | |
438 | */ | |
439 | list_for_each_entry_safe(comp_req, tmp2, | |
440 | &pend_req->group->blkvsc_req_list, | |
441 | req_entry) { | |
f60f2138 S |
442 | |
443 | if (comp_req == pend_req) | |
444 | break; | |
445 | ||
446 | list_del(&comp_req->req_entry); | |
447 | ||
448 | if (comp_req->req) { | |
449 | vm_srb = | |
450 | &comp_req->request.vstor_packet. | |
451 | vm_srb; | |
452 | ret = __blk_end_request(comp_req->req, | |
453 | (!vm_srb->scsi_status ? 0 : -EIO), | |
454 | comp_req->sector_count * | |
455 | blkdev->sector_size); | |
456 | ||
457 | /* FIXME: shouldn't this do more than return? */ | |
458 | if (ret) | |
459 | goto out; | |
460 | } | |
461 | ||
462 | kmem_cache_free(blkdev->request_pool, comp_req); | |
463 | } | |
464 | ||
f60f2138 S |
465 | list_del(&pend_req->pend_entry); |
466 | ||
467 | list_del(&pend_req->req_entry); | |
468 | ||
469 | if (comp_req->req) { | |
470 | if (!__blk_end_request(pend_req->req, -EIO, | |
471 | pend_req->sector_count * | |
472 | blkdev->sector_size)) { | |
473 | /* | |
474 | * All the sectors have been xferred ie the | |
475 | * request is done | |
476 | */ | |
f60f2138 S |
477 | kmem_cache_free(blkdev->request_pool, |
478 | pend_req->group); | |
479 | } | |
480 | } | |
481 | ||
482 | kmem_cache_free(blkdev->request_pool, pend_req); | |
483 | } | |
484 | ||
485 | out: | |
486 | return ret; | |
487 | } | |
488 | ||
8c090044 S |
489 | |
490 | /* | |
491 | * blkvsc_remove() - Callback when our device is removed | |
492 | */ | |
415b023a | 493 | static int blkvsc_remove(struct hv_device *dev) |
8c090044 | 494 | { |
415b023a | 495 | struct block_device_context *blkdev = dev_get_drvdata(&dev->device); |
8c090044 | 496 | unsigned long flags; |
8c090044 | 497 | |
8c090044 S |
498 | |
499 | /* Get to a known state */ | |
500 | spin_lock_irqsave(&blkdev->lock, flags); | |
501 | ||
502 | blkdev->shutting_down = 1; | |
503 | ||
504 | blk_stop_queue(blkdev->gd->queue); | |
505 | ||
67de49c1 S |
506 | blkvsc_cancel_pending_reqs(blkdev); |
507 | ||
8c090044 S |
508 | spin_unlock_irqrestore(&blkdev->lock, flags); |
509 | ||
67de49c1 | 510 | blkvsc_do_operation(blkdev, DO_FLUSH); |
8c090044 | 511 | |
3a450589 S |
512 | if (blkdev->users == 0) { |
513 | del_gendisk(blkdev->gd); | |
514 | put_disk(blkdev->gd); | |
515 | blk_cleanup_queue(blkdev->gd->queue); | |
8c090044 | 516 | |
3a450589 | 517 | storvsc_dev_remove(blkdev->device_ctx); |
8c090044 | 518 | |
3a450589 S |
519 | kmem_cache_destroy(blkdev->request_pool); |
520 | kfree(blkdev); | |
521 | } | |
8c090044 | 522 | |
a31de969 | 523 | return 0; |
8c090044 S |
524 | } |
525 | ||
ca6887fb | 526 | static void blkvsc_shutdown(struct hv_device *dev) |
e82066b2 | 527 | { |
ca6887fb | 528 | struct block_device_context *blkdev = dev_get_drvdata(&dev->device); |
e82066b2 S |
529 | unsigned long flags; |
530 | ||
531 | if (!blkdev) | |
532 | return; | |
533 | ||
e82066b2 S |
534 | spin_lock_irqsave(&blkdev->lock, flags); |
535 | ||
536 | blkdev->shutting_down = 1; | |
537 | ||
538 | blk_stop_queue(blkdev->gd->queue); | |
539 | ||
67de49c1 S |
540 | blkvsc_cancel_pending_reqs(blkdev); |
541 | ||
e82066b2 S |
542 | spin_unlock_irqrestore(&blkdev->lock, flags); |
543 | ||
67de49c1 | 544 | blkvsc_do_operation(blkdev, DO_FLUSH); |
e82066b2 | 545 | |
149b1193 S |
546 | /* |
547 | * Now wait for all outgoing I/O to be drained. | |
548 | */ | |
549 | storvsc_wait_to_drain((struct storvsc_device *)dev->ext); | |
550 | ||
e82066b2 S |
551 | } |
552 | ||
8138bd95 S |
553 | static int blkvsc_release(struct gendisk *disk, fmode_t mode) |
554 | { | |
555 | struct block_device_context *blkdev = disk->private_data; | |
6b73e4c0 | 556 | unsigned long flags; |
8138bd95 | 557 | |
3a450589 S |
558 | spin_lock_irqsave(&blkdev->lock, flags); |
559 | ||
560 | if ((--blkdev->users == 0) && (blkdev->shutting_down)) { | |
561 | blk_stop_queue(blkdev->gd->queue); | |
562 | spin_unlock_irqrestore(&blkdev->lock, flags); | |
563 | ||
26a19739 | 564 | blkvsc_do_operation(blkdev, DO_FLUSH); |
3a450589 S |
565 | del_gendisk(blkdev->gd); |
566 | put_disk(blkdev->gd); | |
567 | blk_cleanup_queue(blkdev->gd->queue); | |
8138bd95 | 568 | |
3a450589 S |
569 | storvsc_dev_remove(blkdev->device_ctx); |
570 | ||
571 | kmem_cache_destroy(blkdev->request_pool); | |
572 | kfree(blkdev); | |
573 | } else | |
574 | spin_unlock_irqrestore(&blkdev->lock, flags); | |
67de49c1 | 575 | |
8138bd95 S |
576 | return 0; |
577 | } | |
578 | ||
87b31c22 | 579 | |
8f8e57ab S |
580 | /* |
581 | * We break the request into 1 or more blkvsc_requests and submit | |
582 | * them. If we cant submit them all, we put them on the | |
583 | * pending_list. The blkvsc_request() will work on the pending_list. | |
584 | */ | |
585 | static int blkvsc_do_request(struct block_device_context *blkdev, | |
586 | struct request *req) | |
587 | { | |
588 | struct bio *bio = NULL; | |
589 | struct bio_vec *bvec = NULL; | |
590 | struct bio_vec *prev_bvec = NULL; | |
591 | struct blkvsc_request *blkvsc_req = NULL; | |
592 | struct blkvsc_request *tmp; | |
593 | int databuf_idx = 0; | |
594 | int seg_idx = 0; | |
595 | sector_t start_sector; | |
596 | unsigned long num_sectors = 0; | |
597 | int ret = 0; | |
598 | int pending = 0; | |
599 | struct blkvsc_request_group *group = NULL; | |
600 | ||
8f8e57ab S |
601 | /* Create a group to tie req to list of blkvsc_reqs */ |
602 | group = kmem_cache_zalloc(blkdev->request_pool, GFP_ATOMIC); | |
603 | if (!group) | |
604 | return -ENOMEM; | |
605 | ||
606 | INIT_LIST_HEAD(&group->blkvsc_req_list); | |
607 | group->outstanding = group->status = 0; | |
608 | ||
609 | start_sector = blk_rq_pos(req); | |
610 | ||
611 | /* foreach bio in the request */ | |
612 | if (req->bio) { | |
613 | for (bio = req->bio; bio; bio = bio->bi_next) { | |
614 | /* | |
615 | * Map this bio into an existing or new storvsc request | |
616 | */ | |
617 | bio_for_each_segment(bvec, bio, seg_idx) { | |
8f8e57ab S |
618 | /* Get a new storvsc request */ |
619 | /* 1st-time */ | |
620 | if ((!blkvsc_req) || | |
621 | (databuf_idx >= MAX_MULTIPAGE_BUFFER_COUNT) | |
622 | /* hole at the begin of page */ | |
623 | || (bvec->bv_offset != 0) || | |
624 | /* hold at the end of page */ | |
625 | (prev_bvec && | |
626 | (prev_bvec->bv_len != PAGE_SIZE))) { | |
627 | /* submit the prev one */ | |
628 | if (blkvsc_req) { | |
629 | blkvsc_req->sector_start = | |
630 | start_sector; | |
631 | sector_div( | |
632 | blkvsc_req->sector_start, | |
633 | (blkdev->sector_size >> 9)); | |
634 | ||
635 | blkvsc_req->sector_count = | |
636 | num_sectors / | |
637 | (blkdev->sector_size >> 9); | |
638 | blkvsc_init_rw(blkvsc_req); | |
639 | } | |
640 | ||
641 | /* | |
642 | * Create new blkvsc_req to represent | |
643 | * the current bvec | |
644 | */ | |
645 | blkvsc_req = | |
646 | kmem_cache_zalloc( | |
647 | blkdev->request_pool, GFP_ATOMIC); | |
648 | if (!blkvsc_req) { | |
649 | /* free up everything */ | |
650 | list_for_each_entry_safe( | |
651 | blkvsc_req, tmp, | |
652 | &group->blkvsc_req_list, | |
653 | req_entry) { | |
654 | list_del( | |
655 | &blkvsc_req->req_entry); | |
656 | kmem_cache_free( | |
657 | blkdev->request_pool, | |
658 | blkvsc_req); | |
659 | } | |
660 | ||
661 | kmem_cache_free( | |
662 | blkdev->request_pool, group); | |
663 | return -ENOMEM; | |
664 | } | |
665 | ||
666 | memset(blkvsc_req, 0, | |
667 | sizeof(struct blkvsc_request)); | |
668 | ||
669 | blkvsc_req->dev = blkdev; | |
670 | blkvsc_req->req = req; | |
671 | blkvsc_req->request. | |
672 | data_buffer.offset | |
673 | = bvec->bv_offset; | |
674 | blkvsc_req->request. | |
675 | data_buffer.len = 0; | |
676 | ||
677 | /* Add to the group */ | |
678 | blkvsc_req->group = group; | |
679 | blkvsc_req->group->outstanding++; | |
680 | list_add_tail(&blkvsc_req->req_entry, | |
681 | &blkvsc_req->group->blkvsc_req_list); | |
682 | ||
683 | start_sector += num_sectors; | |
684 | num_sectors = 0; | |
685 | databuf_idx = 0; | |
686 | } | |
687 | ||
688 | /* | |
689 | * Add the curr bvec/segment to the curr | |
690 | * blkvsc_req | |
691 | */ | |
692 | blkvsc_req->request.data_buffer. | |
693 | pfn_array[databuf_idx] | |
694 | = page_to_pfn(bvec->bv_page); | |
695 | blkvsc_req->request.data_buffer.len | |
696 | += bvec->bv_len; | |
697 | ||
698 | prev_bvec = bvec; | |
699 | ||
700 | databuf_idx++; | |
701 | num_sectors += bvec->bv_len >> 9; | |
702 | ||
703 | } /* bio_for_each_segment */ | |
704 | ||
705 | } /* rq_for_each_bio */ | |
706 | } | |
707 | ||
708 | /* Handle the last one */ | |
709 | if (blkvsc_req) { | |
8f8e57ab S |
710 | blkvsc_req->sector_start = start_sector; |
711 | sector_div(blkvsc_req->sector_start, | |
712 | (blkdev->sector_size >> 9)); | |
713 | ||
714 | blkvsc_req->sector_count = num_sectors / | |
715 | (blkdev->sector_size >> 9); | |
716 | ||
717 | blkvsc_init_rw(blkvsc_req); | |
718 | } | |
719 | ||
720 | list_for_each_entry(blkvsc_req, &group->blkvsc_req_list, req_entry) { | |
721 | if (pending) { | |
8f8e57ab S |
722 | |
723 | list_add_tail(&blkvsc_req->pend_entry, | |
724 | &blkdev->pending_list); | |
725 | } else { | |
726 | ret = blkvsc_submit_request(blkvsc_req, | |
727 | blkvsc_request_completion); | |
d2598f01 | 728 | if (ret == -EAGAIN) { |
8f8e57ab S |
729 | pending = 1; |
730 | list_add_tail(&blkvsc_req->pend_entry, | |
731 | &blkdev->pending_list); | |
732 | } | |
733 | ||
8f8e57ab S |
734 | } |
735 | } | |
736 | ||
737 | return pending; | |
738 | } | |
739 | ||
76abfaa3 S |
740 | static int blkvsc_do_pending_reqs(struct block_device_context *blkdev) |
741 | { | |
742 | struct blkvsc_request *pend_req, *tmp; | |
743 | int ret = 0; | |
744 | ||
745 | /* Flush the pending list first */ | |
746 | list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list, | |
747 | pend_entry) { | |
76abfaa3 S |
748 | |
749 | ret = blkvsc_submit_request(pend_req, | |
750 | blkvsc_request_completion); | |
751 | if (ret != 0) | |
752 | break; | |
753 | else | |
754 | list_del(&pend_req->pend_entry); | |
755 | } | |
756 | ||
757 | return ret; | |
758 | } | |
759 | ||
1efe7058 S |
760 | |
761 | static void blkvsc_request(struct request_queue *queue) | |
762 | { | |
763 | struct block_device_context *blkdev = NULL; | |
764 | struct request *req; | |
765 | int ret = 0; | |
766 | ||
1efe7058 | 767 | while ((req = blk_peek_request(queue)) != NULL) { |
1efe7058 S |
768 | |
769 | blkdev = req->rq_disk->private_data; | |
aac7af6d | 770 | if (blkdev->shutting_down || req->cmd_type != REQ_TYPE_FS) { |
1efe7058 S |
771 | __blk_end_request_cur(req, 0); |
772 | continue; | |
773 | } | |
774 | ||
775 | ret = blkvsc_do_pending_reqs(blkdev); | |
776 | ||
777 | if (ret != 0) { | |
1efe7058 S |
778 | blk_stop_queue(queue); |
779 | break; | |
780 | } | |
781 | ||
782 | blk_start_request(req); | |
783 | ||
784 | ret = blkvsc_do_request(blkdev, req); | |
785 | if (ret > 0) { | |
1efe7058 S |
786 | blk_stop_queue(queue); |
787 | break; | |
788 | } else if (ret < 0) { | |
1efe7058 S |
789 | blk_requeue_request(queue, req); |
790 | blk_stop_queue(queue); | |
791 | break; | |
792 | } | |
793 | } | |
794 | } | |
795 | ||
eefd7f45 | 796 | static const struct hv_vmbus_device_id id_table[] = { |
c45cf2d4 GKH |
797 | /* IDE guid */ |
798 | { VMBUS_DEVICE(0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, | |
799 | 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5) }, | |
800 | { }, | |
eefd7f45 | 801 | }; |
f82bd046 | 802 | |
eefd7f45 | 803 | MODULE_DEVICE_TABLE(vmbus, id_table); |
2ede209f | 804 | |
454f18a9 | 805 | /* The one and only one */ |
428faae1 | 806 | static struct hv_driver blkvsc_drv = { |
768fa219 | 807 | .name = "blkvsc", |
eefd7f45 | 808 | .id_table = id_table, |
428faae1 S |
809 | .probe = blkvsc_probe, |
810 | .remove = blkvsc_remove, | |
811 | .shutdown = blkvsc_shutdown, | |
7e7f0c55 | 812 | }; |
f82bd046 | 813 | |
48c9f7c3 | 814 | static const struct block_device_operations block_ops = { |
f82bd046 HJ |
815 | .owner = THIS_MODULE, |
816 | .open = blkvsc_open, | |
817 | .release = blkvsc_release, | |
f82bd046 HJ |
818 | .getgeo = blkvsc_getgeo, |
819 | .ioctl = blkvsc_ioctl, | |
820 | }; | |
821 | ||
3e189519 | 822 | /* |
8a280399 GKH |
823 | * blkvsc_drv_init - BlkVsc driver initialization. |
824 | */ | |
834702f8 | 825 | static int blkvsc_drv_init(void) |
f82bd046 | 826 | { |
3e326499 | 827 | BUILD_BUG_ON(sizeof(sector_t) != 8); |
768fa219 | 828 | return vmbus_driver_register(&blkvsc_drv); |
f82bd046 HJ |
829 | } |
830 | ||
bd1de709 | 831 | static void blkvsc_drv_exit(void) |
f82bd046 | 832 | { |
768fa219 | 833 | vmbus_driver_unregister(&blkvsc_drv); |
f82bd046 HJ |
834 | } |
835 | ||
3e189519 | 836 | /* |
8a280399 GKH |
837 | * blkvsc_probe - Add a new device for this driver |
838 | */ | |
9efd21e1 | 839 | static int blkvsc_probe(struct hv_device *dev) |
f82bd046 | 840 | { |
8a280399 | 841 | struct block_device_context *blkdev = NULL; |
9f0c7d2c | 842 | struct storvsc_device_info device_info; |
e46ee8ef | 843 | struct storvsc_major_info major_info; |
8a280399 | 844 | int ret = 0; |
f82bd046 | 845 | |
f82bd046 | 846 | blkdev = kzalloc(sizeof(struct block_device_context), GFP_KERNEL); |
8a280399 | 847 | if (!blkdev) { |
f82bd046 | 848 | ret = -ENOMEM; |
d87d707d | 849 | goto cleanup; |
f82bd046 HJ |
850 | } |
851 | ||
852 | INIT_LIST_HEAD(&blkdev->pending_list); | |
853 | ||
454f18a9 | 854 | /* Initialize what we can here */ |
f82bd046 HJ |
855 | spin_lock_init(&blkdev->lock); |
856 | ||
f82bd046 | 857 | |
9efd21e1 | 858 | blkdev->request_pool = kmem_cache_create(dev_name(&dev->device), |
1e05d88e | 859 | sizeof(struct blkvsc_request), 0, |
8a280399 GKH |
860 | SLAB_HWCACHE_ALIGN, NULL); |
861 | if (!blkdev->request_pool) { | |
f82bd046 | 862 | ret = -ENOMEM; |
d87d707d | 863 | goto cleanup; |
f82bd046 HJ |
864 | } |
865 | ||
866 | ||
0d44f5bf | 867 | ret = blkvsc_device_add(dev, &device_info); |
e01c33b8 | 868 | if (ret != 0) |
d87d707d | 869 | goto cleanup; |
f82bd046 | 870 | |
9efd21e1 | 871 | blkdev->device_ctx = dev; |
8a280399 | 872 | /* this identified the device 0 or 1 */ |
8a046024 | 873 | blkdev->target = device_info.target_id; |
8a280399 | 874 | /* this identified the ide ctrl 0 or 1 */ |
8a046024 | 875 | blkdev->path = device_info.path_id; |
f82bd046 | 876 | |
9efd21e1 | 877 | dev_set_drvdata(&dev->device, blkdev); |
f82bd046 | 878 | |
43c51f7d | 879 | ret = storvsc_get_major_info(&device_info, &major_info); |
f82bd046 | 880 | |
e46ee8ef S |
881 | if (ret) |
882 | goto cleanup; | |
883 | ||
884 | if (major_info.do_register) { | |
885 | ret = register_blkdev(major_info.major, major_info.devname); | |
f82bd046 | 886 | |
e46ee8ef S |
887 | if (ret != 0) { |
888 | DPRINT_ERR(BLKVSC_DRV, | |
889 | "register_blkdev() failed! ret %d", ret); | |
890 | goto remove; | |
f82bd046 | 891 | } |
f82bd046 HJ |
892 | } |
893 | ||
e46ee8ef S |
894 | DPRINT_INFO(BLKVSC_DRV, "blkvsc registered for major %d!!", |
895 | major_info.major); | |
f82bd046 HJ |
896 | |
897 | blkdev->gd = alloc_disk(BLKVSC_MINORS); | |
8a280399 | 898 | if (!blkdev->gd) { |
f82bd046 | 899 | ret = -1; |
d87d707d | 900 | goto cleanup; |
f82bd046 HJ |
901 | } |
902 | ||
903 | blkdev->gd->queue = blk_init_queue(blkvsc_request, &blkdev->lock); | |
904 | ||
905 | blk_queue_max_segment_size(blkdev->gd->queue, PAGE_SIZE); | |
8a78362c | 906 | blk_queue_max_segments(blkdev->gd->queue, MAX_MULTIPAGE_BUFFER_COUNT); |
f82bd046 HJ |
907 | blk_queue_segment_boundary(blkdev->gd->queue, PAGE_SIZE-1); |
908 | blk_queue_bounce_limit(blkdev->gd->queue, BLK_BOUNCE_ANY); | |
909 | blk_queue_dma_alignment(blkdev->gd->queue, 511); | |
910 | ||
e46ee8ef S |
911 | blkdev->gd->major = major_info.major; |
912 | if (major_info.index == 1 || major_info.index == 3) | |
f82bd046 HJ |
913 | blkdev->gd->first_minor = BLKVSC_MINORS; |
914 | else | |
915 | blkdev->gd->first_minor = 0; | |
916 | blkdev->gd->fops = &block_ops; | |
917 | blkdev->gd->private_data = blkdev; | |
268eff90 | 918 | blkdev->gd->driverfs_dev = &(blkdev->device_ctx->device); |
e46ee8ef | 919 | sprintf(blkdev->gd->disk_name, "hd%c", 'a' + major_info.index); |
f82bd046 | 920 | |
2ede209f | 921 | blkvsc_do_operation(blkdev, DO_INQUIRY); |
d4919829 | 922 | blkvsc_do_operation(blkdev, DO_CAPACITY); |
f82bd046 HJ |
923 | |
924 | set_capacity(blkdev->gd, blkdev->capacity * (blkdev->sector_size/512)); | |
0fce4c2f | 925 | blk_queue_logical_block_size(blkdev->gd->queue, blkdev->sector_size); |
454f18a9 | 926 | /* go! */ |
f82bd046 HJ |
927 | add_disk(blkdev->gd); |
928 | ||
8a280399 GKH |
929 | DPRINT_INFO(BLKVSC_DRV, "%s added!! capacity %lu sector_size %d", |
930 | blkdev->gd->disk_name, (unsigned long)blkdev->capacity, | |
931 | blkdev->sector_size); | |
f82bd046 HJ |
932 | |
933 | return ret; | |
934 | ||
487ae7cd | 935 | remove: |
3530ef30 | 936 | storvsc_dev_remove(dev); |
f82bd046 | 937 | |
d87d707d | 938 | cleanup: |
8a280399 GKH |
939 | if (blkdev) { |
940 | if (blkdev->request_pool) { | |
f82bd046 HJ |
941 | kmem_cache_destroy(blkdev->request_pool); |
942 | blkdev->request_pool = NULL; | |
943 | } | |
944 | kfree(blkdev); | |
945 | blkdev = NULL; | |
946 | } | |
947 | ||
f82bd046 HJ |
948 | return ret; |
949 | } | |
950 | ||
0b3f6834 | 951 | static void blkvsc_request_completion(struct hv_storvsc_request *request) |
f82bd046 | 952 | { |
8a280399 | 953 | struct blkvsc_request *blkvsc_req = |
d7a1bdb9 | 954 | (struct blkvsc_request *)request->context; |
8a280399 GKH |
955 | struct block_device_context *blkdev = |
956 | (struct block_device_context *)blkvsc_req->dev; | |
f82bd046 HJ |
957 | unsigned long flags; |
958 | struct blkvsc_request *comp_req, *tmp; | |
6dc3f0a7 | 959 | struct vmscsi_request *vm_srb; |
f82bd046 | 960 | |
f82bd046 | 961 | |
f82bd046 HJ |
962 | spin_lock_irqsave(&blkdev->lock, flags); |
963 | ||
964 | blkdev->num_outstanding_reqs--; | |
965 | blkvsc_req->group->outstanding--; | |
966 | ||
454f18a9 BP |
967 | /* |
968 | * Only start processing when all the blkvsc_reqs are | |
969 | * completed. This guarantees no out-of-order blkvsc_req | |
970 | * completion when calling end_that_request_first() | |
971 | */ | |
8a280399 GKH |
972 | if (blkvsc_req->group->outstanding == 0) { |
973 | list_for_each_entry_safe(comp_req, tmp, | |
974 | &blkvsc_req->group->blkvsc_req_list, | |
975 | req_entry) { | |
f82bd046 HJ |
976 | |
977 | list_del(&comp_req->req_entry); | |
978 | ||
6dc3f0a7 | 979 | vm_srb = |
d7a1bdb9 | 980 | &comp_req->request.vstor_packet.vm_srb; |
8a280399 | 981 | if (!__blk_end_request(comp_req->req, |
6dc3f0a7 | 982 | (!vm_srb->scsi_status ? 0 : -EIO), |
8a280399 GKH |
983 | comp_req->sector_count * blkdev->sector_size)) { |
984 | /* | |
985 | * All the sectors have been xferred ie the | |
986 | * request is done | |
987 | */ | |
8a280399 GKH |
988 | kmem_cache_free(blkdev->request_pool, |
989 | comp_req->group); | |
f82bd046 | 990 | } |
f82bd046 HJ |
991 | |
992 | kmem_cache_free(blkdev->request_pool, comp_req); | |
993 | } | |
994 | ||
8a280399 | 995 | if (!blkdev->shutting_down) { |
f82bd046 HJ |
996 | blkvsc_do_pending_reqs(blkdev); |
997 | blk_start_queue(blkdev->gd->queue); | |
998 | blkvsc_request(blkdev->gd->queue); | |
999 | } | |
1000 | } | |
1001 | ||
1002 | spin_unlock_irqrestore(&blkdev->lock, flags); | |
1003 | } | |
1004 | ||
f82bd046 HJ |
1005 | static void __exit blkvsc_exit(void) |
1006 | { | |
f82bd046 | 1007 | blkvsc_drv_exit(); |
f82bd046 HJ |
1008 | } |
1009 | ||
8a280399 | 1010 | MODULE_LICENSE("GPL"); |
26c14cc1 | 1011 | MODULE_VERSION(HV_DRV_VERSION); |
1ec28abb | 1012 | MODULE_DESCRIPTION("Microsoft Hyper-V virtual block driver"); |
154a61cd | 1013 | module_init(blkvsc_drv_init); |
f82bd046 | 1014 | module_exit(blkvsc_exit); |