Browse Source

[hyperv] Cope with Windows Server 2016 enlightenments

An "enlightened" external bootloader (such as Windows Server 2016's
winload.exe) may take ownership of the Hyper-V connection before all
INT 13 operations have been completed.  When this happens, all VMBus
devices are implicitly closed and we are left with a non-functional
network connection.

Detect when our Hyper-V connection has been lost (by checking the
SynIC message page MSR).  Reclaim ownership of the Hyper-V connection
and reestablish any VMBus devices, without disrupting any existing
iPXE state (such as IPv4 settings attached to the network device).

Windows Server 2016 will not cleanly take ownership of an active
Hyper-V connection.  Experimentation shows that we can quiesce by
resetting only the SynIC message page MSR; this results in a
successful SAN boot (on a Windows 2012 R2 physical host).  Choose to
quiesce by resetting (almost) all MSRs, in the hope that this will be
more robust against corner cases such as a stray synthetic interrupt
occurring during the handover.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 7 years ago
parent
commit
b91cc983da

+ 125
- 10
src/arch/x86/drivers/hyperv/hyperv.c View File

40
 #include <ipxe/malloc.h>
40
 #include <ipxe/malloc.h>
41
 #include <ipxe/device.h>
41
 #include <ipxe/device.h>
42
 #include <ipxe/timer.h>
42
 #include <ipxe/timer.h>
43
+#include <ipxe/quiesce.h>
43
 #include <ipxe/cpuid.h>
44
 #include <ipxe/cpuid.h>
44
 #include <ipxe/msr.h>
45
 #include <ipxe/msr.h>
45
 #include <ipxe/hyperv.h>
46
 #include <ipxe/hyperv.h>
299
 	uint64_t siefp;
300
 	uint64_t siefp;
300
 	uint64_t scontrol;
301
 	uint64_t scontrol;
301
 
302
 
303
+	/* Zero SynIC message and event pages */
304
+	memset ( hv->synic.message, 0, PAGE_SIZE );
305
+	memset ( hv->synic.event, 0, PAGE_SIZE );
306
+
302
 	/* Map SynIC message page */
307
 	/* Map SynIC message page */
303
 	simp = rdmsr ( HV_X64_MSR_SIMP );
308
 	simp = rdmsr ( HV_X64_MSR_SIMP );
304
 	simp &= ( PAGE_SIZE - 1 );
309
 	simp &= ( PAGE_SIZE - 1 );
321
 }
326
 }
322
 
327
 
323
 /**
328
 /**
324
- * Unmap synthetic interrupt controller
329
+ * Unmap synthetic interrupt controller, leaving SCONTROL untouched
325
  *
330
  *
326
  * @v hv		Hyper-V hypervisor
331
  * @v hv		Hyper-V hypervisor
327
  */
332
  */
328
-static void hv_unmap_synic ( struct hv_hypervisor *hv ) {
329
-	uint64_t scontrol;
333
+static void hv_unmap_synic_no_scontrol ( struct hv_hypervisor *hv ) {
330
 	uint64_t siefp;
334
 	uint64_t siefp;
331
 	uint64_t simp;
335
 	uint64_t simp;
332
 
336
 
333
-	/* Disable SynIC */
334
-	scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
335
-	scontrol &= ~HV_SCONTROL_ENABLE;
336
-	DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
337
-	wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
338
-
339
 	/* Unmap SynIC event page */
337
 	/* Unmap SynIC event page */
340
 	siefp = rdmsr ( HV_X64_MSR_SIEFP );
338
 	siefp = rdmsr ( HV_X64_MSR_SIEFP );
341
 	siefp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIEFP_ENABLE );
339
 	siefp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIEFP_ENABLE );
349
 	wrmsr ( HV_X64_MSR_SIMP, simp );
347
 	wrmsr ( HV_X64_MSR_SIMP, simp );
350
 }
348
 }
351
 
349
 
350
+/**
351
+ * Unmap synthetic interrupt controller
352
+ *
353
+ * @v hv		Hyper-V hypervisor
354
+ */
355
+static void hv_unmap_synic ( struct hv_hypervisor *hv ) {
356
+	uint64_t scontrol;
357
+
358
+	/* Disable SynIC */
359
+	scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
360
+	scontrol &= ~HV_SCONTROL_ENABLE;
361
+	DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
362
+	wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
363
+
364
+	/* Unmap SynIC event and message pages */
365
+	hv_unmap_synic_no_scontrol ( hv );
366
+}
367
+
352
 /**
368
 /**
353
  * Enable synthetic interrupt
369
  * Enable synthetic interrupt
354
  *
370
  *
385
 	unsigned long msr = HV_X64_MSR_SINT ( sintx );
401
 	unsigned long msr = HV_X64_MSR_SINT ( sintx );
386
 	uint64_t sint;
402
 	uint64_t sint;
387
 
403
 
388
-	/* Disable synthetic interrupt */
404
+	/* Do nothing if interrupt is already disabled */
389
 	sint = rdmsr ( msr );
405
 	sint = rdmsr ( msr );
406
+	if ( sint & HV_SINT_MASKED )
407
+		return;
408
+
409
+	/* Disable synthetic interrupt */
390
 	sint &= ~HV_SINT_AUTO_EOI;
410
 	sint &= ~HV_SINT_AUTO_EOI;
391
 	sint |= HV_SINT_MASKED;
411
 	sint |= HV_SINT_MASKED;
392
 	DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
412
 	DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
589
 	hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
609
 	hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
590
 			NULL );
610
 			NULL );
591
 	free ( hv );
611
 	free ( hv );
612
+	rootdev_set_drvdata ( rootdev, NULL );
592
 }
613
 }
593
 
614
 
594
 /** Hyper-V root device driver */
615
 /** Hyper-V root device driver */
603
 	.driver = &hv_root_driver,
624
 	.driver = &hv_root_driver,
604
 };
625
 };
605
 
626
 
627
+/**
628
+ * Quiesce system
629
+ *
630
+ */
631
+static void hv_quiesce ( void ) {
632
+	struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device );
633
+	unsigned int i;
634
+
635
+	/* Do nothing if we are not running in Hyper-V */
636
+	if ( ! hv )
637
+		return;
638
+
639
+	/* The "enlightened" portions of the Windows Server 2016 boot
640
+	 * process will not cleanly take ownership of an active
641
+	 * Hyper-V connection.  Experimentation shows that the minimum
642
+	 * requirement is that we disable the SynIC message page
643
+	 * (i.e. zero the SIMP MSR).
644
+	 *
645
+	 * We cannot perform a full shutdown of the Hyper-V
646
+	 * connection.  Experimentation shows that if we disable the
647
+	 * SynIC (i.e. zero the SCONTROL MSR) then Windows Server 2016
648
+	 * will enter an indefinite wait loop.
649
+	 *
650
+	 * Attempt to create a safe handover environment by resetting
651
+	 * all MSRs except for SCONTROL.
652
+	 *
653
+	 * Note that we do not shut down our VMBus devices, since we
654
+	 * may need to unquiesce the system and continue operation.
655
+	 */
656
+
657
+	/* Disable all synthetic interrupts */
658
+	for ( i = 0 ; i <= HV_SINT_MAX ; i++ )
659
+		hv_disable_sint ( hv, i );
660
+
661
+	/* Unmap synthetic interrupt controller, leaving SCONTROL
662
+	 * enabled (see above).
663
+	 */
664
+	hv_unmap_synic_no_scontrol ( hv );
665
+
666
+	/* Unmap hypercall page */
667
+	hv_unmap_hypercall ( hv );
668
+
669
+	DBGC ( hv, "HV %p quiesced\n", hv );
670
+}
671
+
672
+/**
673
+ * Unquiesce system
674
+ *
675
+ */
676
+static void hv_unquiesce ( void ) {
677
+	struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device );
678
+	uint64_t simp;
679
+	int rc;
680
+
681
+	/* Do nothing if we are not running in Hyper-V */
682
+	if ( ! hv )
683
+		return;
684
+
685
+	/* Experimentation shows that the "enlightened" portions of
686
+	 * Windows Server 2016 will break our Hyper-V connection at
687
+	 * some point during a SAN boot.  Surprisingly it does not
688
+	 * change the guest OS ID MSR, but it does leave the SynIC
689
+	 * message page disabled.
690
+	 *
691
+	 * Our own explicit quiescing procedure will also disable the
692
+	 * SynIC message page.  We can therefore use the SynIC message
693
+	 * page enable bit as a heuristic to determine when we need to
694
+	 * reestablish our Hyper-V connection.
695
+	 */
696
+	simp = rdmsr ( HV_X64_MSR_SIMP );
697
+	if ( simp & HV_SIMP_ENABLE )
698
+		return;
699
+
700
+	/* Remap hypercall page */
701
+	hv_map_hypercall ( hv );
702
+
703
+	/* Remap synthetic interrupt controller */
704
+	hv_map_synic ( hv );
705
+
706
+	/* Reset Hyper-V devices */
707
+	if ( ( rc = vmbus_reset ( hv, &hv_root_device.dev ) ) != 0 ) {
708
+		DBGC ( hv, "HV %p could not unquiesce: %s\n",
709
+		       hv, strerror ( rc ) );
710
+		/* Nothing we can do */
711
+		return;
712
+	}
713
+}
714
+
715
+/** Hyper-V quiescer */
716
+struct quiescer hv_quiescer __quiescer = {
717
+	.quiesce = hv_quiesce,
718
+	.unquiesce = hv_unquiesce,
719
+};
720
+
606
 /**
721
 /**
607
  * Probe timer
722
  * Probe timer
608
  *
723
  *

+ 47
- 0
src/drivers/net/netvsc.c View File

259
 	struct netvsc_revoke_buffer_message msg;
259
 	struct netvsc_revoke_buffer_message msg;
260
 	int rc;
260
 	int rc;
261
 
261
 
262
+	/* If the buffer's GPADL is obsolete (i.e. was created before
263
+	 * the most recent Hyper-V reset), then we will never receive
264
+	 * a response to the revoke message.  Since the GPADL is
265
+	 * already destroyed as far as the hypervisor is concerned, no
266
+	 * further action is required.
267
+	 */
268
+	if ( netvsc_is_obsolete ( netvsc ) )
269
+		return 0;
270
+
262
 	/* Construct message */
271
 	/* Construct message */
263
 	memset ( &msg, 0, sizeof ( msg ) );
272
 	memset ( &msg, 0, sizeof ( msg ) );
264
 	msg.header.type = cpu_to_le32 ( buffer->revoke_type );
273
 	msg.header.type = cpu_to_le32 ( buffer->revoke_type );
474
 	uint64_t xid;
483
 	uint64_t xid;
475
 	int rc;
484
 	int rc;
476
 
485
 
486
+	/* If the device is obsolete (i.e. was opened before the most
487
+	 * recent Hyper-V reset), then we will never receive transmit
488
+	 * completions.  Fail transmissions immediately to minimise
489
+	 * the delay in closing and reopening the device.
490
+	 */
491
+	if ( netvsc_is_obsolete ( netvsc ) )
492
+		return -EPIPE;
493
+
477
 	/* Sanity check */
494
 	/* Sanity check */
478
 	assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
495
 	assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
479
 	assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) );
496
 	assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) );
823
 	return rc;
840
 	return rc;
824
 }
841
 }
825
 
842
 
843
+/**
844
+ * Reset device
845
+ *
846
+ * @v vmdev		VMBus device
847
+ * @ret rc		Return status code
848
+ */
849
+static int netvsc_reset ( struct vmbus_device *vmdev ) {
850
+	struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
851
+	struct netvsc_device *netvsc = rndis->priv;
852
+	struct net_device *netdev = rndis->netdev;
853
+	int rc;
854
+
855
+	/* A closed device holds no NetVSC (or RNDIS) state, so there
856
+	 * is nothing to reset.
857
+	 */
858
+	if ( ! netdev_is_open ( netdev ) )
859
+		return 0;
860
+
861
+	/* Close and reopen device to reset any stale state */
862
+	netdev_close ( netdev );
863
+	if ( ( rc = netdev_open ( netdev ) ) != 0 ) {
864
+		DBGC ( netvsc, "NETVSC %s could not reopen: %s\n",
865
+		       netvsc->name, strerror ( rc ) );
866
+		return rc;
867
+	}
868
+
869
+	return 0;
870
+}
871
+
826
 /**
872
 /**
827
  * Remove device
873
  * Remove device
828
  *
874
  *
844
 	.type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f,
890
 	.type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f,
845
 			     0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ),
891
 			     0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ),
846
 	.probe = netvsc_probe,
892
 	.probe = netvsc_probe,
893
+	.reset = netvsc_reset,
847
 	.remove = netvsc_remove,
894
 	.remove = netvsc_remove,
848
 };
895
 };

+ 15
- 0
src/drivers/net/netvsc.h View File

362
 	int wait_rc;
362
 	int wait_rc;
363
 };
363
 };
364
 
364
 
365
+/**
366
+ * Check if NetVSC device is obsolete
367
+ *
368
+ * @v netvsc		NetVSC device
369
+ * @v is_obsolete	NetVSC device is obsolete
370
+ *
371
+ * Check if NetVSC device is obsolete (i.e. was opened before the most
372
+ * recent Hyper-V reset).
373
+ */
374
+static inline __attribute__ (( always_inline )) int
375
+netvsc_is_obsolete ( struct netvsc_device *netvsc ) {
376
+
377
+	return vmbus_gpadl_is_obsolete ( netvsc->rx.gpadl );
378
+}
379
+
365
 #endif /* _NETVSC_H */
380
 #endif /* _NETVSC_H */

+ 3
- 0
src/include/ipxe/hyperv.h View File

61
 /** Synthetic interrupt vector mask */
61
 /** Synthetic interrupt vector mask */
62
 #define HV_SINT_VECTOR_MASK HV_SINT_VECTOR ( 0xff )
62
 #define HV_SINT_VECTOR_MASK HV_SINT_VECTOR ( 0xff )
63
 
63
 
64
+/** Maximum synthetic interrupt number */
65
+#define HV_SINT_MAX 15
66
+
64
 /** Post message */
67
 /** Post message */
65
 #define HV_POST_MESSAGE 0x005c
68
 #define HV_POST_MESSAGE 0x005c
66
 
69
 

+ 26
- 0
src/include/ipxe/vmbus.h View File

479
 	/** Hyper-V hypervisor */
479
 	/** Hyper-V hypervisor */
480
 	struct hv_hypervisor *hv;
480
 	struct hv_hypervisor *hv;
481
 
481
 
482
+	/** Channel instance */
483
+	union uuid instance;
482
 	/** Channel ID */
484
 	/** Channel ID */
483
 	unsigned int channel;
485
 	unsigned int channel;
484
 	/** Monitor ID */
486
 	/** Monitor ID */
527
 	 * @ret rc		Return status code
529
 	 * @ret rc		Return status code
528
 	 */
530
 	 */
529
 	int ( * probe ) ( struct vmbus_device *vmdev );
531
 	int ( * probe ) ( struct vmbus_device *vmdev );
532
+	/** Reset device
533
+	 *
534
+	 * @v vmdev		VMBus device
535
+	 * @ret rc		Return status code
536
+	 */
537
+	int ( * reset ) ( struct vmbus_device *vmdev );
530
 	/** Remove device
538
 	/** Remove device
531
 	 *
539
 	 *
532
 	 * @v vmdev		VMBus device
540
 	 * @v vmdev		VMBus device
609
 	list_del ( &pages->list );
617
 	list_del ( &pages->list );
610
 }
618
 }
611
 
619
 
620
+extern unsigned int vmbus_obsolete_gpadl;
621
+
622
+/**
623
+ * Check if GPADL is obsolete
624
+ *
625
+ * @v gpadl		GPADL ID
626
+ * @v is_obsolete	GPADL ID is obsolete
627
+ *
628
+ * Check if GPADL is obsolete (i.e. was created before the most recent
629
+ * Hyper-V reset).
630
+ */
631
+static inline __attribute__ (( always_inline )) int
632
+vmbus_gpadl_is_obsolete ( unsigned int gpadl ) {
633
+
634
+	return ( gpadl <= vmbus_obsolete_gpadl );
635
+}
636
+
612
 extern int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data,
637
 extern int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data,
613
 				   size_t len );
638
 				   size_t len );
614
 extern int vmbus_gpadl_teardown ( struct vmbus_device *vmdev,
639
 extern int vmbus_gpadl_teardown ( struct vmbus_device *vmdev,
629
 extern void vmbus_dump_channel ( struct vmbus_device *vmdev );
654
 extern void vmbus_dump_channel ( struct vmbus_device *vmdev );
630
 
655
 
631
 extern int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent );
656
 extern int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent );
657
+extern int vmbus_reset ( struct hv_hypervisor *hv, struct device *parent );
632
 extern void vmbus_remove ( struct hv_hypervisor *hv, struct device *parent );
658
 extern void vmbus_remove ( struct hv_hypervisor *hv, struct device *parent );
633
 
659
 
634
 #endif /* _IPXE_VMBUS_H */
660
 #endif /* _IPXE_VMBUS_H */

+ 129
- 4
src/interface/hyperv/vmbus.c View File

50
  */
50
  */
51
 #define VMBUS_GPADL_MAGIC 0x18ae0000
51
 #define VMBUS_GPADL_MAGIC 0x18ae0000
52
 
52
 
53
+/** Current (i.e. most recently issued) GPADL ID */
54
+static unsigned int vmbus_gpadl = VMBUS_GPADL_MAGIC;
55
+
56
+/** Obsolete GPADL ID threshold
57
+ *
58
+ * When the Hyper-V connection is reset, any previous GPADLs are
59
+ * automatically rendered obsolete.
60
+ */
61
+unsigned int vmbus_obsolete_gpadl;
62
+
53
 /**
63
 /**
54
  * Post message
64
  * Post message
55
  *
65
  *
281
 		uint64_t pfn[pfn_count];
291
 		uint64_t pfn[pfn_count];
282
 	} __attribute__ (( packed )) gpadlhdr;
292
 	} __attribute__ (( packed )) gpadlhdr;
283
 	const struct vmbus_gpadl_created *created = &vmbus->message->created;
293
 	const struct vmbus_gpadl_created *created = &vmbus->message->created;
284
-	static unsigned int gpadl = VMBUS_GPADL_MAGIC;
294
+	unsigned int gpadl;
285
 	unsigned int i;
295
 	unsigned int i;
286
 	int rc;
296
 	int rc;
287
 
297
 
288
 	/* Allocate GPADL ID */
298
 	/* Allocate GPADL ID */
289
-	gpadl++;
299
+	gpadl = ++vmbus_gpadl;
290
 
300
 
291
 	/* Construct message */
301
 	/* Construct message */
292
 	memset ( &gpadlhdr, 0, sizeof ( gpadlhdr ) );
302
 	memset ( &gpadlhdr, 0, sizeof ( gpadlhdr ) );
347
 	const struct vmbus_gpadl_torndown *torndown = &vmbus->message->torndown;
357
 	const struct vmbus_gpadl_torndown *torndown = &vmbus->message->torndown;
348
 	int rc;
358
 	int rc;
349
 
359
 
360
+	/* If GPADL is obsolete (i.e. was created before the most
361
+	 * recent Hyper-V reset), then we will never receive a
362
+	 * response to the teardown message.  Since the GPADL is
363
+	 * already destroyed as far as the hypervisor is concerned, no
364
+	 * further action is required.
365
+	 */
366
+	if ( vmbus_gpadl_is_obsolete ( gpadl ) )
367
+		return 0;
368
+
350
 	/* Construct message */
369
 	/* Construct message */
351
 	memset ( &teardown, 0, sizeof ( teardown ) );
370
 	memset ( &teardown, 0, sizeof ( teardown ) );
352
 	teardown.header.type = cpu_to_le32 ( VMBUS_GPADL_TEARDOWN );
371
 	teardown.header.type = cpu_to_le32 ( VMBUS_GPADL_TEARDOWN );
530
 	}
549
 	}
531
 
550
 
532
 	/* Tear down GPADL */
551
 	/* Tear down GPADL */
533
-	if ( ( rc = vmbus_gpadl_teardown ( vmdev,
534
-					   vmdev->gpadl ) ) != 0 ) {
552
+	if ( ( rc = vmbus_gpadl_teardown ( vmdev, vmdev->gpadl ) ) != 0 ) {
535
 		DBGC ( vmdev, "VMBUS %s failed to tear down channel GPADL: "
553
 		DBGC ( vmdev, "VMBUS %s failed to tear down channel GPADL: "
536
 		       "%s\n", vmdev->dev.name, strerror ( rc ) );
554
 		       "%s\n", vmdev->dev.name, strerror ( rc ) );
537
 		/* We can't prevent the remote VM from continuing to
555
 		/* We can't prevent the remote VM from continuing to
1187
 					&parent->children );
1205
 					&parent->children );
1188
 			vmdev->dev.parent = parent;
1206
 			vmdev->dev.parent = parent;
1189
 			vmdev->hv = hv;
1207
 			vmdev->hv = hv;
1208
+			memcpy ( &vmdev->instance, &offer->instance,
1209
+				 sizeof ( vmdev->instance ) );
1190
 			vmdev->channel = channel;
1210
 			vmdev->channel = channel;
1191
 			vmdev->monitor = offer->monitor;
1211
 			vmdev->monitor = offer->monitor;
1192
 			vmdev->signal = ( offer->monitored ?
1212
 			vmdev->signal = ( offer->monitored ?
1201
 		} else if ( header->type ==
1221
 		} else if ( header->type ==
1202
 			    cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) {
1222
 			    cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) {
1203
 
1223
 
1224
+			/* End of offer list */
1204
 			break;
1225
 			break;
1205
 
1226
 
1206
 		} else {
1227
 		} else {
1244
 	return rc;
1265
 	return rc;
1245
 }
1266
 }
1246
 
1267
 
1268
+
1269
+/**
1270
+ * Reset channels
1271
+ *
1272
+ * @v hv		Hyper-V hypervisor
1273
+ * @v parent		Parent device
1274
+ * @ret rc		Return status code
1275
+ */
1276
+static int vmbus_reset_channels ( struct hv_hypervisor *hv,
1277
+				  struct device *parent ) {
1278
+	struct vmbus *vmbus = hv->vmbus;
1279
+	const struct vmbus_message_header *header = &vmbus->message->header;
1280
+	const struct vmbus_offer_channel *offer = &vmbus->message->offer;
1281
+	const union uuid *type;
1282
+	struct vmbus_device *vmdev;
1283
+	unsigned int channel;
1284
+	int rc;
1285
+
1286
+	/* Post message */
1287
+	if ( ( rc = vmbus_post_empty_message ( hv, VMBUS_REQUEST_OFFERS ) ) !=0)
1288
+		return rc;
1289
+
1290
+	/* Collect responses */
1291
+	while ( 1 ) {
1292
+
1293
+		/* Wait for response */
1294
+		if ( ( rc = vmbus_wait_for_any_message ( hv ) ) != 0 )
1295
+			return rc;
1296
+
1297
+		/* Handle response */
1298
+		if ( header->type == cpu_to_le32 ( VMBUS_OFFER_CHANNEL ) ) {
1299
+
1300
+			/* Parse offer */
1301
+			type = &offer->type;
1302
+			channel = le32_to_cpu ( offer->channel );
1303
+			DBGC2 ( vmbus, "VMBUS %p offer %d type %s",
1304
+				vmbus, channel, uuid_ntoa ( type ) );
1305
+			if ( offer->monitored )
1306
+				DBGC2 ( vmbus, " monitor %d", offer->monitor );
1307
+			DBGC2 ( vmbus, "\n" );
1308
+
1309
+			/* Do nothing with the offer; we already have all
1310
+			 * of the relevant state from the initial probe.
1311
+			 */
1312
+
1313
+		} else if ( header->type ==
1314
+			    cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) {
1315
+
1316
+			/* End of offer list */
1317
+			break;
1318
+
1319
+		} else {
1320
+			DBGC ( vmbus, "VMBUS %p unexpected offer response type "
1321
+			       "%d\n", vmbus, le32_to_cpu ( header->type ) );
1322
+			return -EPROTO;
1323
+		}
1324
+	}
1325
+
1326
+	/* Reset all devices */
1327
+	list_for_each_entry ( vmdev, &parent->children, dev.siblings ) {
1328
+		if ( ( rc = vmdev->driver->reset ( vmdev ) ) != 0 ) {
1329
+			DBGC ( vmdev, "VMBUS %s could not reset: %s\n",
1330
+			       vmdev->dev.name, strerror ( rc ) );
1331
+			/* Continue attempting to reset other devices */
1332
+			continue;
1333
+		}
1334
+	}
1335
+
1336
+	return 0;
1337
+}
1338
+
1247
 /**
1339
 /**
1248
  * Remove channels
1340
  * Remove channels
1249
  *
1341
  *
1330
 	return rc;
1422
 	return rc;
1331
 }
1423
 }
1332
 
1424
 
1425
+/**
1426
+ * Reset Hyper-V virtual machine bus
1427
+ *
1428
+ * @v hv		Hyper-V hypervisor
1429
+ * @v parent		Parent device
1430
+ * @ret rc		Return status code
1431
+ */
1432
+int vmbus_reset ( struct hv_hypervisor *hv, struct device *parent ) {
1433
+	struct vmbus *vmbus = hv->vmbus;
1434
+	int rc;
1435
+
1436
+	/* Mark all existent GPADLs as obsolete */
1437
+	vmbus_obsolete_gpadl = vmbus_gpadl;
1438
+
1439
+	/* Clear interrupt and monitor pages */
1440
+	memset ( vmbus->intr, 0, PAGE_SIZE );
1441
+	memset ( vmbus->monitor_in, 0, PAGE_SIZE );
1442
+	memset ( vmbus->monitor_out, 0, PAGE_SIZE );
1443
+
1444
+	/* Enable message interrupt */
1445
+	hv_enable_sint ( hv, VMBUS_MESSAGE_SINT );
1446
+
1447
+	/* Renegotiate protocol version */
1448
+	if ( ( rc = vmbus_negotiate_version ( hv ) ) != 0 )
1449
+		return rc;
1450
+
1451
+	/* Reenumerate channels */
1452
+	if ( ( rc = vmbus_reset_channels ( hv, parent ) ) != 0 )
1453
+		return rc;
1454
+
1455
+	return 0;
1456
+}
1457
+
1333
 /**
1458
 /**
1334
  * Remove Hyper-V virtual machine bus
1459
  * Remove Hyper-V virtual machine bus
1335
  *
1460
  *

Loading…
Cancel
Save