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