Staging: hv: fix smp problems in the hyperv core code

This fixes a number of SMP problems that were in the hyperv core code.

Patch originally written by K. Y. Srinivasan <ksrinivasan@novell.com>
but forward ported to the latest in-kernel code and tweaked slightly by
me.

Novell, Inc. hereby disclaims all copyright in any derivative work
copyright associated with this patch.

Signed-off-by: K. Y. Srinivasan <ksrinivasan@novell.com>
Cc: Hank Janssen <hjanssen@microsoft.com>
Cc: Haiyang Zhang <haiyangz@microsoft.com>.
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

+35 -33
+25 -25
drivers/staging/hv/Hv.c
··· 386 * retrieve the initialized message and event pages. Otherwise, we create and 387 * initialize the message and event pages. 388 */ 389 - int HvSynicInit(u32 irqVector) 390 { 391 u64 version; 392 union hv_synic_simp simp; ··· 394 union hv_synic_sint sharedSint; 395 union hv_synic_scontrol sctrl; 396 u64 guestID; 397 - int ret = 0; 398 399 DPRINT_ENTER(VMBUS); 400 401 if (!gHvContext.HypercallPage) { 402 DPRINT_EXIT(VMBUS); 403 - return ret; 404 } 405 406 /* Check the version */ ··· 426 */ 427 rdmsrl(HV_X64_MSR_GUEST_OS_ID, guestID); 428 if (guestID == HV_LINUX_GUEST_ID) { 429 - gHvContext.synICMessagePage[0] = 430 phys_to_virt(simp.BaseSimpGpa << PAGE_SHIFT); 431 - gHvContext.synICEventPage[0] = 432 phys_to_virt(siefp.BaseSiefpGpa << PAGE_SHIFT); 433 } else { 434 DPRINT_ERR(VMBUS, "unknown guest id!!"); 435 goto Cleanup; 436 } 437 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p", 438 - gHvContext.synICMessagePage[0], 439 - gHvContext.synICEventPage[0]); 440 } else { 441 - gHvContext.synICMessagePage[0] = osd_PageAlloc(1); 442 - if (gHvContext.synICMessagePage[0] == NULL) { 443 DPRINT_ERR(VMBUS, 444 "unable to allocate SYNIC message page!!"); 445 goto Cleanup; 446 } 447 448 - gHvContext.synICEventPage[0] = osd_PageAlloc(1); 449 - if (gHvContext.synICEventPage[0] == NULL) { 450 DPRINT_ERR(VMBUS, 451 "unable to allocate SYNIC event page!!"); 452 goto Cleanup; ··· 455 /* Setup the Synic's message page */ 456 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64); 457 simp.SimpEnabled = 1; 458 - simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[0]) 459 >> PAGE_SHIFT; 460 461 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", ··· 466 /* Setup the Synic's event page */ 467 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64); 468 siefp.SiefpEnabled = 1; 469 - siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[0]) 470 >> PAGE_SHIFT; 471 472 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", ··· 502 503 DPRINT_EXIT(VMBUS); 504 505 - return ret; 506 507 Cleanup: 508 - ret = -1; 509 - 510 if (gHvContext.GuestId == HV_LINUX_GUEST_ID) { 511 - if (gHvContext.synICEventPage[0]) 512 - osd_PageFree(gHvContext.synICEventPage[0], 1); 513 514 - if (gHvContext.synICMessagePage[0]) 515 - osd_PageFree(gHvContext.synICMessagePage[0], 1); 516 } 517 518 DPRINT_EXIT(VMBUS); 519 - 520 - return ret; 521 } 522 523 /** 524 * HvSynicCleanup - Cleanup routine for HvSynicInit(). 525 */ 526 - void HvSynicCleanup(void) 527 { 528 union hv_synic_sint sharedSint; 529 union hv_synic_simp simp; 530 union hv_synic_siefp siefp; 531 532 DPRINT_ENTER(VMBUS); 533 ··· 538 539 sharedSint.Masked = 1; 540 541 /* Disable the interrupt */ 542 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64); 543 ··· 560 561 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64); 562 563 - osd_PageFree(gHvContext.synICMessagePage[0], 1); 564 - osd_PageFree(gHvContext.synICEventPage[0], 1); 565 } 566 567 DPRINT_EXIT(VMBUS);
··· 386 * retrieve the initialized message and event pages. Otherwise, we create and 387 * initialize the message and event pages. 388 */ 389 + void HvSynicInit(void *irqarg) 390 { 391 u64 version; 392 union hv_synic_simp simp; ··· 394 union hv_synic_sint sharedSint; 395 union hv_synic_scontrol sctrl; 396 u64 guestID; 397 + u32 irqVector = *((u32 *)(irqarg)); 398 + int cpu = smp_processor_id(); 399 400 DPRINT_ENTER(VMBUS); 401 402 if (!gHvContext.HypercallPage) { 403 DPRINT_EXIT(VMBUS); 404 + return; 405 } 406 407 /* Check the version */ ··· 425 */ 426 rdmsrl(HV_X64_MSR_GUEST_OS_ID, guestID); 427 if (guestID == HV_LINUX_GUEST_ID) { 428 + gHvContext.synICMessagePage[cpu] = 429 phys_to_virt(simp.BaseSimpGpa << PAGE_SHIFT); 430 + gHvContext.synICEventPage[cpu] = 431 phys_to_virt(siefp.BaseSiefpGpa << PAGE_SHIFT); 432 } else { 433 DPRINT_ERR(VMBUS, "unknown guest id!!"); 434 goto Cleanup; 435 } 436 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p", 437 + gHvContext.synICMessagePage[cpu], 438 + gHvContext.synICEventPage[cpu]); 439 } else { 440 + gHvContext.synICMessagePage[cpu] = (void *)get_zeroed_page(GFP_ATOMIC); 441 + if (gHvContext.synICMessagePage[cpu] == NULL) { 442 DPRINT_ERR(VMBUS, 443 "unable to allocate SYNIC message page!!"); 444 goto Cleanup; 445 } 446 447 + gHvContext.synICEventPage[cpu] = (void *)get_zeroed_page(GFP_ATOMIC); 448 + if (gHvContext.synICEventPage[cpu] == NULL) { 449 DPRINT_ERR(VMBUS, 450 "unable to allocate SYNIC event page!!"); 451 goto Cleanup; ··· 454 /* Setup the Synic's message page */ 455 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64); 456 simp.SimpEnabled = 1; 457 + simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[cpu]) 458 >> PAGE_SHIFT; 459 460 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", ··· 465 /* Setup the Synic's event page */ 466 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64); 467 siefp.SiefpEnabled = 1; 468 + siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[cpu]) 469 >> PAGE_SHIFT; 470 471 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", ··· 501 502 DPRINT_EXIT(VMBUS); 503 504 + return; 505 506 Cleanup: 507 if (gHvContext.GuestId == HV_LINUX_GUEST_ID) { 508 + if (gHvContext.synICEventPage[cpu]) 509 + osd_PageFree(gHvContext.synICEventPage[cpu], 1); 510 511 + if (gHvContext.synICMessagePage[cpu]) 512 + osd_PageFree(gHvContext.synICMessagePage[cpu], 1); 513 } 514 515 DPRINT_EXIT(VMBUS); 516 + return; 517 } 518 519 /** 520 * HvSynicCleanup - Cleanup routine for HvSynicInit(). 521 */ 522 + void HvSynicCleanup(void *arg) 523 { 524 union hv_synic_sint sharedSint; 525 union hv_synic_simp simp; 526 union hv_synic_siefp siefp; 527 + int cpu = smp_processor_id(); 528 529 DPRINT_ENTER(VMBUS); 530 ··· 539 540 sharedSint.Masked = 1; 541 542 + /* Need to correctly cleanup in the case of SMP!!! */ 543 /* Disable the interrupt */ 544 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64); 545 ··· 560 561 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64); 562 563 + osd_PageFree(gHvContext.synICMessagePage[cpu], 1); 564 + osd_PageFree(gHvContext.synICEventPage[cpu], 1); 565 } 566 567 DPRINT_EXIT(VMBUS);
+3 -3
drivers/staging/hv/Hv.h
··· 93 }, 94 }; 95 96 - #define MAX_NUM_CPUS 1 97 98 99 struct hv_input_signal_event_buffer { ··· 137 138 extern u16 HvSignalEvent(void); 139 140 - extern int HvSynicInit(u32 irqVector); 141 142 - extern void HvSynicCleanup(void); 143 144 #endif /* __HV_H__ */
··· 93 }, 94 }; 95 96 + #define MAX_NUM_CPUS 32 97 98 99 struct hv_input_signal_event_buffer { ··· 137 138 extern u16 HvSignalEvent(void); 139 140 + extern void HvSynicInit(void *irqarg); 141 142 + extern void HvSynicCleanup(void *arg); 143 144 #endif /* __HV_H__ */
+7 -5
drivers/staging/hv/Vmbus.c
··· 129 130 /* strcpy(dev->name, "vmbus"); */ 131 /* SynIC setup... */ 132 - ret = HvSynicInit(*irqvector); 133 134 /* Connect to VMBus in the root partition */ 135 ret = VmbusConnect(); ··· 150 DPRINT_ENTER(VMBUS); 151 VmbusChannelReleaseUnattachedChannels(); 152 VmbusDisconnect(); 153 - HvSynicCleanup(); 154 DPRINT_EXIT(VMBUS); 155 156 return ret; ··· 173 */ 174 static void VmbusOnMsgDPC(struct hv_driver *drv) 175 { 176 - void *page_addr = gHvContext.synICMessagePage[0]; 177 struct hv_message *msg = (struct hv_message *)page_addr + 178 VMBUS_MESSAGE_SINT; 179 struct hv_message *copied; ··· 231 static int VmbusOnISR(struct hv_driver *drv) 232 { 233 int ret = 0; 234 void *page_addr; 235 struct hv_message *msg; 236 union hv_synic_event_flags *event; 237 238 - page_addr = gHvContext.synICMessagePage[0]; 239 msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; 240 241 DPRINT_ENTER(VMBUS); ··· 250 } 251 252 /* TODO: Check if there are events to be process */ 253 - page_addr = gHvContext.synICEventPage[0]; 254 event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; 255 256 /* Since we are a child, we only need to check bit 0 */
··· 129 130 /* strcpy(dev->name, "vmbus"); */ 131 /* SynIC setup... */ 132 + on_each_cpu(HvSynicInit, (void *)irqvector, 1); 133 134 /* Connect to VMBus in the root partition */ 135 ret = VmbusConnect(); ··· 150 DPRINT_ENTER(VMBUS); 151 VmbusChannelReleaseUnattachedChannels(); 152 VmbusDisconnect(); 153 + on_each_cpu(HvSynicCleanup, NULL, 1); 154 DPRINT_EXIT(VMBUS); 155 156 return ret; ··· 173 */ 174 static void VmbusOnMsgDPC(struct hv_driver *drv) 175 { 176 + int cpu = smp_processor_id(); 177 + void *page_addr = gHvContext.synICMessagePage[cpu]; 178 struct hv_message *msg = (struct hv_message *)page_addr + 179 VMBUS_MESSAGE_SINT; 180 struct hv_message *copied; ··· 230 static int VmbusOnISR(struct hv_driver *drv) 231 { 232 int ret = 0; 233 + int cpu = smp_processor_id(); 234 void *page_addr; 235 struct hv_message *msg; 236 union hv_synic_event_flags *event; 237 238 + page_addr = gHvContext.synICMessagePage[cpu]; 239 msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; 240 241 DPRINT_ENTER(VMBUS); ··· 248 } 249 250 /* TODO: Check if there are events to be process */ 251 + page_addr = gHvContext.synICEventPage[cpu]; 252 event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; 253 254 /* Since we are a child, we only need to check bit 0 */