hv_storvsc_drv_freebsd.c (307302) | hv_storvsc_drv_freebsd.c (307307) |
---|---|
1/*- 2 * Copyright (c) 2009-2012,2016 Microsoft Corp. 3 * Copyright (c) 2012 NetApp Inc. 4 * Copyright (c) 2012 Citrix Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions --- 19 unchanged lines hidden (view full) --- 28 29/** 30 * StorVSC driver for Hyper-V. This driver presents a SCSI HBA interface 31 * to the Comman Access Method (CAM) layer. CAM control blocks (CCBs) are 32 * converted into VSCSI protocol messages which are delivered to the parent 33 * partition StorVSP driver over the Hyper-V VMBUS. 34 */ 35#include <sys/cdefs.h> | 1/*- 2 * Copyright (c) 2009-2012,2016 Microsoft Corp. 3 * Copyright (c) 2012 NetApp Inc. 4 * Copyright (c) 2012 Citrix Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions --- 19 unchanged lines hidden (view full) --- 28 29/** 30 * StorVSC driver for Hyper-V. This driver presents a SCSI HBA interface 31 * to the Comman Access Method (CAM) layer. CAM control blocks (CCBs) are 32 * converted into VSCSI protocol messages which are delivered to the parent 33 * partition StorVSP driver over the Hyper-V VMBUS. 34 */ 35#include <sys/cdefs.h> |
36__FBSDID("$FreeBSD: stable/11/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c 307302 2016-10-14 08:02:37Z sephe $"); | 36__FBSDID("$FreeBSD: stable/11/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c 307307 2016-10-14 08:45:53Z sephe $"); |
37 38#include <sys/param.h> 39#include <sys/proc.h> 40#include <sys/condvar.h> 41#include <sys/time.h> 42#include <sys/systm.h> 43#include <sys/sockio.h> 44#include <sys/mbuf.h> --- 75 unchanged lines hidden (view full) --- 120 struct callout callout; 121 struct sema synch_sema; /*Synchronize the request/response if needed */ 122 struct sglist *bounce_sgl; 123 unsigned int bounce_sgl_count; 124 uint64_t not_aligned_seg_bits; 125}; 126 127struct storvsc_softc { | 37 38#include <sys/param.h> 39#include <sys/proc.h> 40#include <sys/condvar.h> 41#include <sys/time.h> 42#include <sys/systm.h> 43#include <sys/sockio.h> 44#include <sys/mbuf.h> --- 75 unchanged lines hidden (view full) --- 120 struct callout callout; 121 struct sema synch_sema; /*Synchronize the request/response if needed */ 122 struct sglist *bounce_sgl; 123 unsigned int bounce_sgl_count; 124 uint64_t not_aligned_seg_bits; 125}; 126 127struct storvsc_softc { |
128 struct hv_device *hs_dev; | 128 struct hv_vmbus_channel *hs_chan; |
129 LIST_HEAD(, hv_storvsc_request) hs_free_list; 130 struct mtx hs_lock; 131 struct storvsc_driver_props *hs_drv_props; 132 int hs_unit; 133 uint32_t hs_frozen; 134 struct cam_sim *hs_sim; 135 struct cam_path *hs_path; 136 uint32_t hs_num_out_reqs; 137 boolean_t hs_destroy; 138 boolean_t hs_drain_notify; 139 struct sema hs_drain_sema; 140 struct hv_storvsc_request hs_init_req; 141 struct hv_storvsc_request hs_reset_req; | 129 LIST_HEAD(, hv_storvsc_request) hs_free_list; 130 struct mtx hs_lock; 131 struct storvsc_driver_props *hs_drv_props; 132 int hs_unit; 133 uint32_t hs_frozen; 134 struct cam_sim *hs_sim; 135 struct cam_path *hs_path; 136 uint32_t hs_num_out_reqs; 137 boolean_t hs_destroy; 138 boolean_t hs_drain_notify; 139 struct sema hs_drain_sema; 140 struct hv_storvsc_request hs_init_req; 141 struct hv_storvsc_request hs_reset_req; |
142 device_t hs_dev; |
|
142}; 143 144 145/** 146 * HyperV storvsc timeout testing cases: 147 * a. IO returned after first timeout; 148 * b. IO returned after second timeout and queue freeze; 149 * c. IO returned while timer handler is running --- 109 unchanged lines hidden (view full) --- 259static int storvsc_attach(device_t dev); 260static int storvsc_detach(device_t dev); 261static void storvsc_poll(struct cam_sim * sim); 262static void storvsc_action(struct cam_sim * sim, union ccb * ccb); 263static int create_storvsc_request(union ccb *ccb, struct hv_storvsc_request *reqp); 264static void storvsc_free_request(struct storvsc_softc *sc, struct hv_storvsc_request *reqp); 265static enum hv_storage_type storvsc_get_storage_type(device_t dev); 266static void hv_storvsc_rescan_target(struct storvsc_softc *sc); | 143}; 144 145 146/** 147 * HyperV storvsc timeout testing cases: 148 * a. IO returned after first timeout; 149 * b. IO returned after second timeout and queue freeze; 150 * c. IO returned while timer handler is running --- 109 unchanged lines hidden (view full) --- 260static int storvsc_attach(device_t dev); 261static int storvsc_detach(device_t dev); 262static void storvsc_poll(struct cam_sim * sim); 263static void storvsc_action(struct cam_sim * sim, union ccb * ccb); 264static int create_storvsc_request(union ccb *ccb, struct hv_storvsc_request *reqp); 265static void storvsc_free_request(struct storvsc_softc *sc, struct hv_storvsc_request *reqp); 266static enum hv_storage_type storvsc_get_storage_type(device_t dev); 267static void hv_storvsc_rescan_target(struct storvsc_softc *sc); |
267static void hv_storvsc_on_channel_callback(void *context); | 268static void hv_storvsc_on_channel_callback(void *xchan); |
268static void hv_storvsc_on_iocompletion( struct storvsc_softc *sc, 269 struct vstor_packet *vstor_packet, 270 struct hv_storvsc_request *request); | 269static void hv_storvsc_on_iocompletion( struct storvsc_softc *sc, 270 struct vstor_packet *vstor_packet, 271 struct hv_storvsc_request *request); |
271static int hv_storvsc_connect_vsp(struct hv_device *device); | 272static int hv_storvsc_connect_vsp(struct storvsc_softc *); |
272static void storvsc_io_done(struct hv_storvsc_request *reqp); 273static void storvsc_copy_sgl_to_bounce_buf(struct sglist *bounce_sgl, 274 bus_dma_segment_t *orig_sgl, 275 unsigned int orig_sgl_count, 276 uint64_t seg_bits); 277void storvsc_copy_from_bounce_buf_to_sgl(bus_dma_segment_t *dest_sgl, 278 unsigned int dest_sgl_count, 279 struct sglist* src_sgl, --- 12 unchanged lines hidden (view full) --- 292 "storvsc", storvsc_methods, sizeof(struct storvsc_softc), 293}; 294 295static devclass_t storvsc_devclass; 296DRIVER_MODULE(storvsc, vmbus, storvsc_driver, storvsc_devclass, 0, 0); 297MODULE_VERSION(storvsc, 1); 298MODULE_DEPEND(storvsc, vmbus, 1, 1, 1); 299 | 273static void storvsc_io_done(struct hv_storvsc_request *reqp); 274static void storvsc_copy_sgl_to_bounce_buf(struct sglist *bounce_sgl, 275 bus_dma_segment_t *orig_sgl, 276 unsigned int orig_sgl_count, 277 uint64_t seg_bits); 278void storvsc_copy_from_bounce_buf_to_sgl(bus_dma_segment_t *dest_sgl, 279 unsigned int dest_sgl_count, 280 struct sglist* src_sgl, --- 12 unchanged lines hidden (view full) --- 293 "storvsc", storvsc_methods, sizeof(struct storvsc_softc), 294}; 295 296static devclass_t storvsc_devclass; 297DRIVER_MODULE(storvsc, vmbus, storvsc_driver, storvsc_devclass, 0, 0); 298MODULE_VERSION(storvsc, 1); 299MODULE_DEPEND(storvsc, vmbus, 1, 1, 1); 300 |
300 301/** 302 * The host is capable of sending messages to us that are 303 * completely unsolicited. So, we need to address the race 304 * condition where we may be in the process of unloading the 305 * driver when the host may send us an unsolicited message. 306 * We address this issue by implementing a sequentially 307 * consistent protocol: 308 * 309 * 1. Channel callback is invoked while holding the channel lock 310 * and an unloading driver will reset the channel callback under 311 * the protection of this channel lock. 312 * 313 * 2. To ensure bounded wait time for unloading a driver, we don't 314 * permit outgoing traffic once the device is marked as being 315 * destroyed. 316 * 317 * 3. Once the device is marked as being destroyed, we only 318 * permit incoming traffic to properly account for 319 * packets already sent out. 320 */ 321static inline struct storvsc_softc * 322get_stor_device(struct hv_device *device, 323 boolean_t outbound) 324{ 325 struct storvsc_softc *sc; 326 327 sc = device_get_softc(device->device); 328 329 if (outbound) { 330 /* 331 * Here we permit outgoing I/O only 332 * if the device is not being destroyed. 333 */ 334 335 if (sc->hs_destroy) { 336 sc = NULL; 337 } 338 } else { 339 /* 340 * inbound case; if being destroyed 341 * only permit to account for 342 * messages already sent out. 343 */ 344 if (sc->hs_destroy && (sc->hs_num_out_reqs == 0)) { 345 sc = NULL; 346 } 347 } 348 return sc; 349} 350 | |
351static void | 301static void |
352storvsc_subchan_attach(struct hv_vmbus_channel *new_channel) | 302storvsc_subchan_attach(struct storvsc_softc *sc, 303 struct hv_vmbus_channel *new_channel) |
353{ | 304{ |
354 struct hv_device *device; 355 struct storvsc_softc *sc; | |
356 struct vmstor_chan_props props; 357 int ret = 0; 358 | 305 struct vmstor_chan_props props; 306 int ret = 0; 307 |
359 device = new_channel->device; 360 sc = get_stor_device(device, TRUE); 361 if (sc == NULL) 362 return; 363 | |
364 memset(&props, 0, sizeof(props)); 365 | 308 memset(&props, 0, sizeof(props)); 309 |
310 new_channel->hv_chan_priv1 = sc; |
|
366 vmbus_channel_cpu_rr(new_channel); 367 ret = hv_vmbus_channel_open(new_channel, 368 sc->hs_drv_props->drv_ringbuffer_size, 369 sc->hs_drv_props->drv_ringbuffer_size, 370 (void *)&props, 371 sizeof(struct vmstor_chan_props), 372 hv_storvsc_on_channel_callback, 373 new_channel); | 311 vmbus_channel_cpu_rr(new_channel); 312 ret = hv_vmbus_channel_open(new_channel, 313 sc->hs_drv_props->drv_ringbuffer_size, 314 sc->hs_drv_props->drv_ringbuffer_size, 315 (void *)&props, 316 sizeof(struct vmstor_chan_props), 317 hv_storvsc_on_channel_callback, 318 new_channel); |
374 375 return; | |
376} 377 378/** 379 * @brief Send multi-channel creation request to host 380 * 381 * @param device a Hyper-V device pointer 382 * @param max_chans the max channels supported by vmbus 383 */ 384static void | 319} 320 321/** 322 * @brief Send multi-channel creation request to host 323 * 324 * @param device a Hyper-V device pointer 325 * @param max_chans the max channels supported by vmbus 326 */ 327static void |
385storvsc_send_multichannel_request(struct hv_device *dev, int max_chans) | 328storvsc_send_multichannel_request(struct storvsc_softc *sc, int max_chans) |
386{ 387 struct hv_vmbus_channel **subchan; | 329{ 330 struct hv_vmbus_channel **subchan; |
388 struct storvsc_softc *sc; | |
389 struct hv_storvsc_request *request; 390 struct vstor_packet *vstor_packet; 391 int request_channels_cnt = 0; 392 int ret, i; 393 394 /* get multichannels count that need to create */ 395 request_channels_cnt = MIN(max_chans, mp_ncpus); 396 | 331 struct hv_storvsc_request *request; 332 struct vstor_packet *vstor_packet; 333 int request_channels_cnt = 0; 334 int ret, i; 335 336 /* get multichannels count that need to create */ 337 request_channels_cnt = MIN(max_chans, mp_ncpus); 338 |
397 sc = get_stor_device(dev, TRUE); 398 if (sc == NULL) { 399 printf("Storvsc_error: get sc failed while send mutilchannel " 400 "request\n"); 401 return; 402 } 403 | |
404 request = &sc->hs_init_req; 405 406 /* request the host to create multi-channel */ 407 memset(request, 0, sizeof(struct hv_storvsc_request)); 408 409 sema_init(&request->synch_sema, 0, ("stor_synch_sema")); 410 411 vstor_packet = &request->vstor_packet; 412 413 vstor_packet->operation = VSTOR_OPERATION_CREATE_MULTI_CHANNELS; 414 vstor_packet->flags = REQUEST_COMPLETION_FLAG; 415 vstor_packet->u.multi_channels_cnt = request_channels_cnt; 416 417 ret = hv_vmbus_channel_send_packet( | 339 request = &sc->hs_init_req; 340 341 /* request the host to create multi-channel */ 342 memset(request, 0, sizeof(struct hv_storvsc_request)); 343 344 sema_init(&request->synch_sema, 0, ("stor_synch_sema")); 345 346 vstor_packet = &request->vstor_packet; 347 348 vstor_packet->operation = VSTOR_OPERATION_CREATE_MULTI_CHANNELS; 349 vstor_packet->flags = REQUEST_COMPLETION_FLAG; 350 vstor_packet->u.multi_channels_cnt = request_channels_cnt; 351 352 ret = hv_vmbus_channel_send_packet( |
418 dev->channel, | 353 sc->hs_chan, |
419 vstor_packet, 420 VSTOR_PKT_SIZE, 421 (uint64_t)(uintptr_t)request, 422 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 423 HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 424 425 /* wait for 5 seconds */ 426 ret = sema_timedwait(&request->synch_sema, 5 * hz); --- 7 unchanged lines hidden (view full) --- 434 vstor_packet->status != 0) { 435 printf("Storvsc_error: create multi-channel invalid operation " 436 "(%d) or statue (%u)\n", 437 vstor_packet->operation, vstor_packet->status); 438 return; 439 } 440 441 /* Wait for sub-channels setup to complete. */ | 354 vstor_packet, 355 VSTOR_PKT_SIZE, 356 (uint64_t)(uintptr_t)request, 357 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 358 HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 359 360 /* wait for 5 seconds */ 361 ret = sema_timedwait(&request->synch_sema, 5 * hz); --- 7 unchanged lines hidden (view full) --- 369 vstor_packet->status != 0) { 370 printf("Storvsc_error: create multi-channel invalid operation " 371 "(%d) or statue (%u)\n", 372 vstor_packet->operation, vstor_packet->status); 373 return; 374 } 375 376 /* Wait for sub-channels setup to complete. */ |
442 subchan = vmbus_get_subchan(dev->channel, request_channels_cnt); | 377 subchan = vmbus_get_subchan(sc->hs_chan, request_channels_cnt); |
443 444 /* Attach the sub-channels. */ 445 for (i = 0; i < request_channels_cnt; ++i) | 378 379 /* Attach the sub-channels. */ 380 for (i = 0; i < request_channels_cnt; ++i) |
446 storvsc_subchan_attach(subchan[i]); | 381 storvsc_subchan_attach(sc, subchan[i]); |
447 448 /* Release the sub-channels. */ 449 vmbus_rel_subchan(subchan, request_channels_cnt); 450 451 if (bootverbose) 452 printf("Storvsc create multi-channel success!\n"); 453} 454 455/** 456 * @brief initialize channel connection to parent partition 457 * 458 * @param dev a Hyper-V device pointer 459 * @returns 0 on success, non-zero error on failure 460 */ 461static int | 382 383 /* Release the sub-channels. */ 384 vmbus_rel_subchan(subchan, request_channels_cnt); 385 386 if (bootverbose) 387 printf("Storvsc create multi-channel success!\n"); 388} 389 390/** 391 * @brief initialize channel connection to parent partition 392 * 393 * @param dev a Hyper-V device pointer 394 * @returns 0 on success, non-zero error on failure 395 */ 396static int |
462hv_storvsc_channel_init(struct hv_device *dev) | 397hv_storvsc_channel_init(struct storvsc_softc *sc) |
463{ 464 int ret = 0, i; 465 struct hv_storvsc_request *request; 466 struct vstor_packet *vstor_packet; | 398{ 399 int ret = 0, i; 400 struct hv_storvsc_request *request; 401 struct vstor_packet *vstor_packet; |
467 struct storvsc_softc *sc; | |
468 uint16_t max_chans = 0; 469 boolean_t support_multichannel = FALSE; 470 uint32_t version; 471 472 max_chans = 0; 473 support_multichannel = FALSE; 474 | 402 uint16_t max_chans = 0; 403 boolean_t support_multichannel = FALSE; 404 uint32_t version; 405 406 max_chans = 0; 407 support_multichannel = FALSE; 408 |
475 sc = get_stor_device(dev, TRUE); 476 if (sc == NULL) 477 return (ENODEV); 478 | |
479 request = &sc->hs_init_req; 480 memset(request, 0, sizeof(struct hv_storvsc_request)); 481 vstor_packet = &request->vstor_packet; 482 request->softc = sc; 483 484 /** 485 * Initiate the vsc/vsp initialization protocol on the open channel 486 */ 487 sema_init(&request->synch_sema, 0, ("stor_synch_sema")); 488 489 vstor_packet->operation = VSTOR_OPERATION_BEGININITIALIZATION; 490 vstor_packet->flags = REQUEST_COMPLETION_FLAG; 491 492 493 ret = hv_vmbus_channel_send_packet( | 409 request = &sc->hs_init_req; 410 memset(request, 0, sizeof(struct hv_storvsc_request)); 411 vstor_packet = &request->vstor_packet; 412 request->softc = sc; 413 414 /** 415 * Initiate the vsc/vsp initialization protocol on the open channel 416 */ 417 sema_init(&request->synch_sema, 0, ("stor_synch_sema")); 418 419 vstor_packet->operation = VSTOR_OPERATION_BEGININITIALIZATION; 420 vstor_packet->flags = REQUEST_COMPLETION_FLAG; 421 422 423 ret = hv_vmbus_channel_send_packet( |
494 dev->channel, | 424 sc->hs_chan, |
495 vstor_packet, 496 VSTOR_PKT_SIZE, 497 (uint64_t)(uintptr_t)request, 498 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 499 HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 500 501 if (ret != 0) 502 goto cleanup; --- 17 unchanged lines hidden (view full) --- 520 521 vstor_packet->u.version.major_minor = 522 vmstor_proto_list[i].proto_version; 523 524 /* revision is only significant for Windows guests */ 525 vstor_packet->u.version.revision = 0; 526 527 ret = hv_vmbus_channel_send_packet( | 425 vstor_packet, 426 VSTOR_PKT_SIZE, 427 (uint64_t)(uintptr_t)request, 428 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 429 HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 430 431 if (ret != 0) 432 goto cleanup; --- 17 unchanged lines hidden (view full) --- 450 451 vstor_packet->u.version.major_minor = 452 vmstor_proto_list[i].proto_version; 453 454 /* revision is only significant for Windows guests */ 455 vstor_packet->u.version.revision = 0; 456 457 ret = hv_vmbus_channel_send_packet( |
528 dev->channel, | 458 sc->hs_chan, |
529 vstor_packet, 530 VSTOR_PKT_SIZE, 531 (uint64_t)(uintptr_t)request, 532 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 533 HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 534 535 if (ret != 0) 536 goto cleanup; --- 26 unchanged lines hidden (view full) --- 563 /** 564 * Query channel properties 565 */ 566 memset(vstor_packet, 0, sizeof(struct vstor_packet)); 567 vstor_packet->operation = VSTOR_OPERATION_QUERYPROPERTIES; 568 vstor_packet->flags = REQUEST_COMPLETION_FLAG; 569 570 ret = hv_vmbus_channel_send_packet( | 459 vstor_packet, 460 VSTOR_PKT_SIZE, 461 (uint64_t)(uintptr_t)request, 462 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 463 HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 464 465 if (ret != 0) 466 goto cleanup; --- 26 unchanged lines hidden (view full) --- 493 /** 494 * Query channel properties 495 */ 496 memset(vstor_packet, 0, sizeof(struct vstor_packet)); 497 vstor_packet->operation = VSTOR_OPERATION_QUERYPROPERTIES; 498 vstor_packet->flags = REQUEST_COMPLETION_FLAG; 499 500 ret = hv_vmbus_channel_send_packet( |
571 dev->channel, | 501 sc->hs_chan, |
572 vstor_packet, 573 VSTOR_PKT_SIZE, 574 (uint64_t)(uintptr_t)request, 575 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 576 HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 577 578 if ( ret != 0) 579 goto cleanup; --- 7 unchanged lines hidden (view full) --- 587 /* TODO: Check returned version */ 588 if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO || 589 vstor_packet->status != 0) { 590 goto cleanup; 591 } 592 593 /* multi-channels feature is supported by WIN8 and above version */ 594 max_chans = vstor_packet->u.chan_props.max_channel_cnt; | 502 vstor_packet, 503 VSTOR_PKT_SIZE, 504 (uint64_t)(uintptr_t)request, 505 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 506 HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 507 508 if ( ret != 0) 509 goto cleanup; --- 7 unchanged lines hidden (view full) --- 517 /* TODO: Check returned version */ 518 if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO || 519 vstor_packet->status != 0) { 520 goto cleanup; 521 } 522 523 /* multi-channels feature is supported by WIN8 and above version */ 524 max_chans = vstor_packet->u.chan_props.max_channel_cnt; |
595 version = VMBUS_GET_VERSION(device_get_parent(dev->device), 596 dev->device); | 525 version = VMBUS_GET_VERSION(device_get_parent(sc->hs_dev), sc->hs_dev); |
597 if (version != VMBUS_VERSION_WIN7 && version != VMBUS_VERSION_WS2008 && 598 (vstor_packet->u.chan_props.flags & 599 HV_STORAGE_SUPPORTS_MULTI_CHANNEL)) { 600 support_multichannel = TRUE; 601 } 602 603 memset(vstor_packet, 0, sizeof(struct vstor_packet)); 604 vstor_packet->operation = VSTOR_OPERATION_ENDINITIALIZATION; 605 vstor_packet->flags = REQUEST_COMPLETION_FLAG; 606 607 ret = hv_vmbus_channel_send_packet( | 526 if (version != VMBUS_VERSION_WIN7 && version != VMBUS_VERSION_WS2008 && 527 (vstor_packet->u.chan_props.flags & 528 HV_STORAGE_SUPPORTS_MULTI_CHANNEL)) { 529 support_multichannel = TRUE; 530 } 531 532 memset(vstor_packet, 0, sizeof(struct vstor_packet)); 533 vstor_packet->operation = VSTOR_OPERATION_ENDINITIALIZATION; 534 vstor_packet->flags = REQUEST_COMPLETION_FLAG; 535 536 ret = hv_vmbus_channel_send_packet( |
608 dev->channel, | 537 sc->hs_chan, |
609 vstor_packet, 610 VSTOR_PKT_SIZE, 611 (uint64_t)(uintptr_t)request, 612 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 613 HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 614 615 if (ret != 0) { 616 goto cleanup; --- 9 unchanged lines hidden (view full) --- 626 vstor_packet->status != 0) 627 goto cleanup; 628 629 /* 630 * If multi-channel is supported, send multichannel create 631 * request to host. 632 */ 633 if (support_multichannel) | 538 vstor_packet, 539 VSTOR_PKT_SIZE, 540 (uint64_t)(uintptr_t)request, 541 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 542 HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 543 544 if (ret != 0) { 545 goto cleanup; --- 9 unchanged lines hidden (view full) --- 555 vstor_packet->status != 0) 556 goto cleanup; 557 558 /* 559 * If multi-channel is supported, send multichannel create 560 * request to host. 561 */ 562 if (support_multichannel) |
634 storvsc_send_multichannel_request(dev, max_chans); | 563 storvsc_send_multichannel_request(sc, max_chans); |
635 636cleanup: 637 sema_destroy(&request->synch_sema); 638 return (ret); 639} 640 641/** 642 * @brief Open channel connection to paraent partition StorVSP driver 643 * 644 * Open and initialize channel connection to parent partition StorVSP driver. 645 * 646 * @param pointer to a Hyper-V device 647 * @returns 0 on success, non-zero error on failure 648 */ 649static int | 564 565cleanup: 566 sema_destroy(&request->synch_sema); 567 return (ret); 568} 569 570/** 571 * @brief Open channel connection to paraent partition StorVSP driver 572 * 573 * Open and initialize channel connection to parent partition StorVSP driver. 574 * 575 * @param pointer to a Hyper-V device 576 * @returns 0 on success, non-zero error on failure 577 */ 578static int |
650hv_storvsc_connect_vsp(struct hv_device *dev) | 579hv_storvsc_connect_vsp(struct storvsc_softc *sc) |
651{ 652 int ret = 0; 653 struct vmstor_chan_props props; | 580{ 581 int ret = 0; 582 struct vmstor_chan_props props; |
654 struct storvsc_softc *sc; | |
655 | 583 |
656 sc = device_get_softc(dev->device); 657 | |
658 memset(&props, 0, sizeof(struct vmstor_chan_props)); 659 660 /* 661 * Open the channel 662 */ | 584 memset(&props, 0, sizeof(struct vmstor_chan_props)); 585 586 /* 587 * Open the channel 588 */ |
663 vmbus_channel_cpu_rr(dev->channel); | 589 KASSERT(sc->hs_chan->hv_chan_priv1 == sc, ("invalid chan priv1")); 590 vmbus_channel_cpu_rr(sc->hs_chan); |
664 ret = hv_vmbus_channel_open( | 591 ret = hv_vmbus_channel_open( |
665 dev->channel, | 592 sc->hs_chan, |
666 sc->hs_drv_props->drv_ringbuffer_size, 667 sc->hs_drv_props->drv_ringbuffer_size, 668 (void *)&props, 669 sizeof(struct vmstor_chan_props), 670 hv_storvsc_on_channel_callback, | 593 sc->hs_drv_props->drv_ringbuffer_size, 594 sc->hs_drv_props->drv_ringbuffer_size, 595 (void *)&props, 596 sizeof(struct vmstor_chan_props), 597 hv_storvsc_on_channel_callback, |
671 dev->channel); | 598 sc->hs_chan); |
672 673 if (ret != 0) { 674 return ret; 675 } 676 | 599 600 if (ret != 0) { 601 return ret; 602 } 603 |
677 ret = hv_storvsc_channel_init(dev); | 604 ret = hv_storvsc_channel_init(sc); |
678 679 return (ret); 680} 681 682#if HVS_HOST_RESET 683static int | 605 606 return (ret); 607} 608 609#if HVS_HOST_RESET 610static int |
684hv_storvsc_host_reset(struct hv_device *dev) | 611hv_storvsc_host_reset(struct storvsc_softc *sc) |
685{ 686 int ret = 0; | 612{ 613 int ret = 0; |
687 struct storvsc_softc *sc; | |
688 689 struct hv_storvsc_request *request; 690 struct vstor_packet *vstor_packet; 691 | 614 615 struct hv_storvsc_request *request; 616 struct vstor_packet *vstor_packet; 617 |
692 sc = get_stor_device(dev, TRUE); 693 if (sc == NULL) { 694 return ENODEV; 695 } 696 | |
697 request = &sc->hs_reset_req; 698 request->softc = sc; 699 vstor_packet = &request->vstor_packet; 700 701 sema_init(&request->synch_sema, 0, "stor synch sema"); 702 703 vstor_packet->operation = VSTOR_OPERATION_RESETBUS; 704 vstor_packet->flags = REQUEST_COMPLETION_FLAG; --- 30 unchanged lines hidden (view full) --- 735/** 736 * @brief Function to initiate an I/O request 737 * 738 * @param device Hyper-V device pointer 739 * @param request pointer to a request structure 740 * @returns 0 on success, non-zero error on failure 741 */ 742static int | 618 request = &sc->hs_reset_req; 619 request->softc = sc; 620 vstor_packet = &request->vstor_packet; 621 622 sema_init(&request->synch_sema, 0, "stor synch sema"); 623 624 vstor_packet->operation = VSTOR_OPERATION_RESETBUS; 625 vstor_packet->flags = REQUEST_COMPLETION_FLAG; --- 30 unchanged lines hidden (view full) --- 656/** 657 * @brief Function to initiate an I/O request 658 * 659 * @param device Hyper-V device pointer 660 * @param request pointer to a request structure 661 * @returns 0 on success, non-zero error on failure 662 */ 663static int |
743hv_storvsc_io_request(struct hv_device *device, | 664hv_storvsc_io_request(struct storvsc_softc *sc, |
744 struct hv_storvsc_request *request) 745{ | 665 struct hv_storvsc_request *request) 666{ |
746 struct storvsc_softc *sc; | |
747 struct vstor_packet *vstor_packet = &request->vstor_packet; 748 struct hv_vmbus_channel* outgoing_channel = NULL; 749 int ret = 0; 750 | 667 struct vstor_packet *vstor_packet = &request->vstor_packet; 668 struct hv_vmbus_channel* outgoing_channel = NULL; 669 int ret = 0; 670 |
751 sc = get_stor_device(device, TRUE); 752 753 if (sc == NULL) { 754 return ENODEV; 755 } 756 | |
757 vstor_packet->flags |= REQUEST_COMPLETION_FLAG; 758 759 vstor_packet->u.vm_srb.length = 760 sizeof(struct vmscsi_req) - vmscsi_size_delta; 761 762 vstor_packet->u.vm_srb.sense_info_len = sense_buffer_size; 763 764 vstor_packet->u.vm_srb.transfer_len = request->data_buf.length; 765 766 vstor_packet->operation = VSTOR_OPERATION_EXECUTESRB; 767 | 671 vstor_packet->flags |= REQUEST_COMPLETION_FLAG; 672 673 vstor_packet->u.vm_srb.length = 674 sizeof(struct vmscsi_req) - vmscsi_size_delta; 675 676 vstor_packet->u.vm_srb.sense_info_len = sense_buffer_size; 677 678 vstor_packet->u.vm_srb.transfer_len = request->data_buf.length; 679 680 vstor_packet->operation = VSTOR_OPERATION_EXECUTESRB; 681 |
768 outgoing_channel = vmbus_select_outgoing_channel(device->channel); | 682 outgoing_channel = vmbus_select_outgoing_channel(sc->hs_chan); |
769 770 mtx_unlock(&request->softc->hs_lock); 771 if (request->data_buf.length) { 772 ret = hv_vmbus_channel_send_packet_multipagebuffer( 773 outgoing_channel, 774 &request->data_buf, 775 vstor_packet, 776 VSTOR_PKT_SIZE, --- 95 unchanged lines hidden (view full) --- 872 ccb->ccb_h.func_code = XPT_SCAN_BUS; 873 else 874 ccb->ccb_h.func_code = XPT_SCAN_TGT; 875 876 xpt_rescan(ccb); 877} 878 879static void | 683 684 mtx_unlock(&request->softc->hs_lock); 685 if (request->data_buf.length) { 686 ret = hv_vmbus_channel_send_packet_multipagebuffer( 687 outgoing_channel, 688 &request->data_buf, 689 vstor_packet, 690 VSTOR_PKT_SIZE, --- 95 unchanged lines hidden (view full) --- 786 ccb->ccb_h.func_code = XPT_SCAN_BUS; 787 else 788 ccb->ccb_h.func_code = XPT_SCAN_TGT; 789 790 xpt_rescan(ccb); 791} 792 793static void |
880hv_storvsc_on_channel_callback(void *context) | 794hv_storvsc_on_channel_callback(void *xchan) |
881{ 882 int ret = 0; | 795{ 796 int ret = 0; |
883 hv_vmbus_channel *channel = (hv_vmbus_channel *)context; 884 struct hv_device *device = NULL; 885 struct storvsc_softc *sc; | 797 hv_vmbus_channel *channel = xchan; 798 struct storvsc_softc *sc = channel->hv_chan_priv1; |
886 uint32_t bytes_recvd; 887 uint64_t request_id; 888 uint8_t packet[roundup2(sizeof(struct vstor_packet), 8)]; 889 struct hv_storvsc_request *request; 890 struct vstor_packet *vstor_packet; 891 | 799 uint32_t bytes_recvd; 800 uint64_t request_id; 801 uint8_t packet[roundup2(sizeof(struct vstor_packet), 8)]; 802 struct hv_storvsc_request *request; 803 struct vstor_packet *vstor_packet; 804 |
892 device = channel->device; 893 KASSERT(device, ("device is NULL")); 894 895 sc = get_stor_device(device, FALSE); 896 if (sc == NULL) { 897 printf("Storvsc_error: get stor device failed.\n"); 898 return; 899 } 900 | |
901 ret = hv_vmbus_channel_recv_packet( 902 channel, 903 packet, 904 roundup2(VSTOR_PKT_SIZE, 8), 905 &bytes_recvd, 906 &request_id); 907 908 while ((ret == 0) && (bytes_recvd > 0)) { --- 87 unchanged lines hidden (view full) --- 996 * be used for SCSI device peripherals. 997 * 998 * @param a device 999 * @returns 0 on success or an error on failure 1000 */ 1001static int 1002storvsc_attach(device_t dev) 1003{ | 805 ret = hv_vmbus_channel_recv_packet( 806 channel, 807 packet, 808 roundup2(VSTOR_PKT_SIZE, 8), 809 &bytes_recvd, 810 &request_id); 811 812 while ((ret == 0) && (bytes_recvd > 0)) { --- 87 unchanged lines hidden (view full) --- 900 * be used for SCSI device peripherals. 901 * 902 * @param a device 903 * @returns 0 on success or an error on failure 904 */ 905static int 906storvsc_attach(device_t dev) 907{ |
1004 struct hv_device *hv_dev = vmbus_get_devctx(dev); | |
1005 enum hv_storage_type stor_type; 1006 struct storvsc_softc *sc; 1007 struct cam_devq *devq; 1008 int ret, i, j; 1009 struct hv_storvsc_request *reqp; 1010 struct root_hold_token *root_mount_token = NULL; 1011 struct hv_sgl_node *sgl_node = NULL; 1012 void *tmp_buff = NULL; 1013 1014 /* 1015 * We need to serialize storvsc attach calls. 1016 */ 1017 root_mount_token = root_mount_hold("storvsc"); 1018 1019 sc = device_get_softc(dev); | 908 enum hv_storage_type stor_type; 909 struct storvsc_softc *sc; 910 struct cam_devq *devq; 911 int ret, i, j; 912 struct hv_storvsc_request *reqp; 913 struct root_hold_token *root_mount_token = NULL; 914 struct hv_sgl_node *sgl_node = NULL; 915 void *tmp_buff = NULL; 916 917 /* 918 * We need to serialize storvsc attach calls. 919 */ 920 root_mount_token = root_mount_hold("storvsc"); 921 922 sc = device_get_softc(dev); |
923 sc->hs_chan = vmbus_get_channel(dev); 924 sc->hs_chan->hv_chan_priv1 = sc; |
|
1020 1021 stor_type = storvsc_get_storage_type(dev); 1022 1023 if (stor_type == DRIVER_UNKNOWN) { 1024 ret = ENODEV; 1025 goto cleanup; 1026 } 1027 1028 /* fill in driver specific properties */ 1029 sc->hs_drv_props = &g_drv_props_table[stor_type]; 1030 1031 /* fill in device specific properties */ 1032 sc->hs_unit = device_get_unit(dev); | 925 926 stor_type = storvsc_get_storage_type(dev); 927 928 if (stor_type == DRIVER_UNKNOWN) { 929 ret = ENODEV; 930 goto cleanup; 931 } 932 933 /* fill in driver specific properties */ 934 sc->hs_drv_props = &g_drv_props_table[stor_type]; 935 936 /* fill in device specific properties */ 937 sc->hs_unit = device_get_unit(dev); |
1033 sc->hs_dev = hv_dev; | 938 sc->hs_dev = dev; |
1034 1035 LIST_INIT(&sc->hs_free_list); 1036 mtx_init(&sc->hs_lock, "hvslck", NULL, MTX_DEF); 1037 1038 for (i = 0; i < sc->hs_drv_props->drv_max_ios_per_target; ++i) { 1039 reqp = malloc(sizeof(struct hv_storvsc_request), 1040 M_DEVBUF, M_WAITOK|M_ZERO); 1041 reqp->softc = sc; --- 32 unchanged lines hidden (view full) --- 1074 sgl_node, link); 1075 } 1076 } 1077 1078 sc->hs_destroy = FALSE; 1079 sc->hs_drain_notify = FALSE; 1080 sema_init(&sc->hs_drain_sema, 0, "Store Drain Sema"); 1081 | 939 940 LIST_INIT(&sc->hs_free_list); 941 mtx_init(&sc->hs_lock, "hvslck", NULL, MTX_DEF); 942 943 for (i = 0; i < sc->hs_drv_props->drv_max_ios_per_target; ++i) { 944 reqp = malloc(sizeof(struct hv_storvsc_request), 945 M_DEVBUF, M_WAITOK|M_ZERO); 946 reqp->softc = sc; --- 32 unchanged lines hidden (view full) --- 979 sgl_node, link); 980 } 981 } 982 983 sc->hs_destroy = FALSE; 984 sc->hs_drain_notify = FALSE; 985 sema_init(&sc->hs_drain_sema, 0, "Store Drain Sema"); 986 |
1082 ret = hv_storvsc_connect_vsp(hv_dev); | 987 ret = hv_storvsc_connect_vsp(sc); |
1083 if (ret != 0) { 1084 goto cleanup; 1085 } 1086 1087 /* 1088 * Create the device queue. 1089 * Hyper-V maps each target to one SCSI HBA 1090 */ --- 81 unchanged lines hidden (view full) --- 1172 * @param dev a device 1173 * returns 0 on success 1174 */ 1175static int 1176storvsc_detach(device_t dev) 1177{ 1178 struct storvsc_softc *sc = device_get_softc(dev); 1179 struct hv_storvsc_request *reqp = NULL; | 988 if (ret != 0) { 989 goto cleanup; 990 } 991 992 /* 993 * Create the device queue. 994 * Hyper-V maps each target to one SCSI HBA 995 */ --- 81 unchanged lines hidden (view full) --- 1077 * @param dev a device 1078 * returns 0 on success 1079 */ 1080static int 1081storvsc_detach(device_t dev) 1082{ 1083 struct storvsc_softc *sc = device_get_softc(dev); 1084 struct hv_storvsc_request *reqp = NULL; |
1180 struct hv_device *hv_device = vmbus_get_devctx(dev); | |
1181 struct hv_sgl_node *sgl_node = NULL; 1182 int j = 0; 1183 1184 sc->hs_destroy = TRUE; 1185 1186 /* 1187 * At this point, all outbound traffic should be disabled. We 1188 * only allow inbound traffic (responses) to proceed so that --- 5 unchanged lines hidden (view full) --- 1194 sc->hs_drain_notify = FALSE; 1195 1196 /* 1197 * Since we have already drained, we don't need to busy wait. 1198 * The call to close the channel will reset the callback 1199 * under the protection of the incoming channel lock. 1200 */ 1201 | 1085 struct hv_sgl_node *sgl_node = NULL; 1086 int j = 0; 1087 1088 sc->hs_destroy = TRUE; 1089 1090 /* 1091 * At this point, all outbound traffic should be disabled. We 1092 * only allow inbound traffic (responses) to proceed so that --- 5 unchanged lines hidden (view full) --- 1098 sc->hs_drain_notify = FALSE; 1099 1100 /* 1101 * Since we have already drained, we don't need to busy wait. 1102 * The call to close the channel will reset the callback 1103 * under the protection of the incoming channel lock. 1104 */ 1105 |
1202 hv_vmbus_channel_close(hv_device->channel); | 1106 hv_vmbus_channel_close(sc->hs_chan); |
1203 1204 mtx_lock(&sc->hs_lock); 1205 while (!LIST_EMPTY(&sc->hs_free_list)) { 1206 reqp = LIST_FIRST(&sc->hs_free_list); 1207 LIST_REMOVE(reqp, link); 1208 1209 free(reqp, M_DEVBUF); 1210 } --- 37 unchanged lines hidden (view full) --- 1248 1249 if (reqp->vstor_packet.vm_srb.cdb[0] != opcode) { 1250 return; 1251 } 1252 1253 if (wait) { 1254 mtx_lock(&reqp->event.mtx); 1255 } | 1107 1108 mtx_lock(&sc->hs_lock); 1109 while (!LIST_EMPTY(&sc->hs_free_list)) { 1110 reqp = LIST_FIRST(&sc->hs_free_list); 1111 LIST_REMOVE(reqp, link); 1112 1113 free(reqp, M_DEVBUF); 1114 } --- 37 unchanged lines hidden (view full) --- 1152 1153 if (reqp->vstor_packet.vm_srb.cdb[0] != opcode) { 1154 return; 1155 } 1156 1157 if (wait) { 1158 mtx_lock(&reqp->event.mtx); 1159 } |
1256 ret = hv_storvsc_io_request(sc->hs_dev, reqp); | 1160 ret = hv_storvsc_io_request(sc, reqp); |
1257 if (ret != 0) { 1258 if (wait) { 1259 mtx_unlock(&reqp->event.mtx); 1260 } 1261 printf("%s: io_request failed with %d.\n", 1262 __func__, ret); 1263 ccb->ccb_h.status = CAM_PROVIDE_FAIL; 1264 mtx_lock(&sc->hs_lock); --- 94 unchanged lines hidden (view full) --- 1359 */ 1360static void 1361storvsc_poll(struct cam_sim *sim) 1362{ 1363 struct storvsc_softc *sc = cam_sim_softc(sim); 1364 1365 mtx_assert(&sc->hs_lock, MA_OWNED); 1366 mtx_unlock(&sc->hs_lock); | 1161 if (ret != 0) { 1162 if (wait) { 1163 mtx_unlock(&reqp->event.mtx); 1164 } 1165 printf("%s: io_request failed with %d.\n", 1166 __func__, ret); 1167 ccb->ccb_h.status = CAM_PROVIDE_FAIL; 1168 mtx_lock(&sc->hs_lock); --- 94 unchanged lines hidden (view full) --- 1263 */ 1264static void 1265storvsc_poll(struct cam_sim *sim) 1266{ 1267 struct storvsc_softc *sc = cam_sim_softc(sim); 1268 1269 mtx_assert(&sc->hs_lock, MA_OWNED); 1270 mtx_unlock(&sc->hs_lock); |
1367 hv_storvsc_on_channel_callback(sc->hs_dev->channel); | 1271 hv_storvsc_on_channel_callback(sc->hs_chan); |
1368 mtx_lock(&sc->hs_lock); 1369} 1370 1371/** 1372 * @brief StorVSC device action function 1373 * 1374 * This function is responsible for handling SCSI operations which 1375 * are passed from the CAM layer. The requests are in the form of --- 68 unchanged lines hidden (view full) --- 1444 case XPT_CALC_GEOMETRY:{ 1445 cam_calc_geometry(&ccb->ccg, 1); 1446 xpt_done(ccb); 1447 return; 1448 } 1449 case XPT_RESET_BUS: 1450 case XPT_RESET_DEV:{ 1451#if HVS_HOST_RESET | 1272 mtx_lock(&sc->hs_lock); 1273} 1274 1275/** 1276 * @brief StorVSC device action function 1277 * 1278 * This function is responsible for handling SCSI operations which 1279 * are passed from the CAM layer. The requests are in the form of --- 68 unchanged lines hidden (view full) --- 1348 case XPT_CALC_GEOMETRY:{ 1349 cam_calc_geometry(&ccb->ccg, 1); 1350 xpt_done(ccb); 1351 return; 1352 } 1353 case XPT_RESET_BUS: 1354 case XPT_RESET_DEV:{ 1355#if HVS_HOST_RESET |
1452 if ((res = hv_storvsc_host_reset(sc->hs_dev)) != 0) { | 1356 if ((res = hv_storvsc_host_reset(sc)) != 0) { |
1453 xpt_print(ccb->ccb_h.path, 1454 "hv_storvsc_host_reset failed with %d\n", res); 1455 ccb->ccb_h.status = CAM_PROVIDE_FAIL; 1456 xpt_done(ccb); 1457 return; 1458 } 1459 ccb->ccb_h.status = CAM_REQ_CMP; 1460 xpt_done(ccb); --- 56 unchanged lines hidden (view full) --- 1517 return; 1518 default: 1519 break; 1520 } 1521#endif /* HVS_TIMEOUT_TEST */ 1522 } 1523#endif 1524 | 1357 xpt_print(ccb->ccb_h.path, 1358 "hv_storvsc_host_reset failed with %d\n", res); 1359 ccb->ccb_h.status = CAM_PROVIDE_FAIL; 1360 xpt_done(ccb); 1361 return; 1362 } 1363 ccb->ccb_h.status = CAM_REQ_CMP; 1364 xpt_done(ccb); --- 56 unchanged lines hidden (view full) --- 1421 return; 1422 default: 1423 break; 1424 } 1425#endif /* HVS_TIMEOUT_TEST */ 1426 } 1427#endif 1428 |
1525 if ((res = hv_storvsc_io_request(sc->hs_dev, reqp)) != 0) { | 1429 if ((res = hv_storvsc_io_request(sc, reqp)) != 0) { |
1526 xpt_print(ccb->ccb_h.path, 1527 "hv_storvsc_io_request failed with %d\n", res); 1528 ccb->ccb_h.status = CAM_PROVIDE_FAIL; 1529 storvsc_free_request(sc, reqp); 1530 xpt_done(ccb); 1531 return; 1532 } 1533 return; --- 609 unchanged lines hidden (view full) --- 2143 * SCSI or BlkVSC (paravirtual IDE) device. 2144 * 2145 * @param dev a device 2146 * returns an enum 2147 */ 2148static enum hv_storage_type 2149storvsc_get_storage_type(device_t dev) 2150{ | 1430 xpt_print(ccb->ccb_h.path, 1431 "hv_storvsc_io_request failed with %d\n", res); 1432 ccb->ccb_h.status = CAM_PROVIDE_FAIL; 1433 storvsc_free_request(sc, reqp); 1434 xpt_done(ccb); 1435 return; 1436 } 1437 return; --- 609 unchanged lines hidden (view full) --- 2047 * SCSI or BlkVSC (paravirtual IDE) device. 2048 * 2049 * @param dev a device 2050 * returns an enum 2051 */ 2052static enum hv_storage_type 2053storvsc_get_storage_type(device_t dev) 2054{ |
2151 const char *p = vmbus_get_type(dev); | 2055 device_t parent = device_get_parent(dev); |
2152 | 2056 |
2153 if (!memcmp(p, &gBlkVscDeviceType, sizeof(hv_guid))) { | 2057 if (VMBUS_PROBE_GUID(parent, dev, &gBlkVscDeviceType) == 0) |
2154 return DRIVER_BLKVSC; | 2058 return DRIVER_BLKVSC; |
2155 } else if (!memcmp(p, &gStorVscDeviceType, sizeof(hv_guid))) { | 2059 if (VMBUS_PROBE_GUID(parent, dev, &gStorVscDeviceType) == 0) |
2156 return DRIVER_STORVSC; | 2060 return DRIVER_STORVSC; |
2157 } 2158 return (DRIVER_UNKNOWN); | 2061 return DRIVER_UNKNOWN; |
2159} | 2062} |
2160 | |