Changeset 28829 in vbox for trunk/src/VBox/HostDrivers/VBoxNetFlt
- Timestamp:
- Apr 27, 2010 2:05:08 PM (15 years ago)
- svn:sync-xref-src-repo-rev:
- 60745
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c
r28800 r28829 18 18 /** @page pg_netflt VBoxNetFlt - Network Interface Filter 19 19 * 20 * This is a kernel module that attaches to a real interface on the host 21 * andfilters and injects packets.20 * This is a kernel module that attaches to a real interface on the host and 21 * filters and injects packets. 22 22 * 23 23 * In the big picture we're one of the three trunk interface on the internal … … 25 25 * 26 26 * 27 * @section sec_netflt_msc Locking / Sequence Diagrams 27 * @section sec_netflt_locking Locking and Potential Races 28 * 29 * The main challenge here is to make sure the netfilter and internal network 30 * instances won't be destroyed while someone is calling into them. 31 * 32 * The main calls into or out of of the filter driver are: 33 * - Send. 34 * - Async send completion (not implemented yet) 35 * - Release by the internal network. 36 * - Receive. 37 * - Disappearance of the host networking interface. 38 * - Reappearance of the host networking interface. 39 * 40 * The latter two calls are can be caused by driver unloading/loading or the 41 * device being physical unplugged (e.g. a USB network device). Actually, the 42 * unload scenario must fervently be prevent as it will cause panics because the 43 * internal network will assume the trunk is around until it releases it. 44 * @todo Need to figure which host allow unloading and block/fix it. 45 * 46 * Currently the netfilter instance lives until the internal network releases 47 * it. So, it is the internal networks responsibility to make sure there are no 48 * active calls when it releases the trunk and destroys the network. The 49 * netfilter assists in this by providing INTNETTRUNKIFPORT::pfnSetState and 50 * INTNETTRUNKIFPORT::pfnWaitForIdle. The trunk state is used to enable/disable 51 * promiscuous mode on the hardware NIC (or similar activation) as well 52 * indicating that disconnect is imminent and no further calls shall be made 53 * into the internal network. After changing the state to disconnecting and 54 * prior to invoking INTNETTRUNKIFPORT::pfnDisconnectAndRelease, the internal 55 * network will use INTNETTRUNKIFPORT::pfnWaitForIdle to wait for any still 56 * active calls to complete. 57 * 58 * The netfilter employs a busy counter and an internal state in addition to the 59 * public trunk state. All these variables are protected using a spinlock. 60 * 61 * 62 * @section sec_netflt_msc Locking / Sequence Diagrams - OBSOLETE 63 * 64 * !OBSOLETE! - THIS WAS THE OLD APPROACH! 28 65 * 29 66 * This secion contains a few sequence diagrams describing the problematic … … 32 69 * The thing that makes it all a bit problematic is that multiple events may 33 70 * happen at the same time, and that we have to be very careful to avoid 34 * deadlocks caused by mixing our locks with the ones in the host kernel. 35 * The main events are receive, send, async send completion, disappearance of36 * the host networking interface and it's reappearance. The latter two events37 * arecan be caused by driver unloading/loading or the device being physical71 * deadlocks caused by mixing our locks with the ones in the host kernel. The 72 * main events are receive, send, async send completion, disappearance of the 73 * host networking interface and its reappearance. The latter two events are 74 * can be caused by driver unloading/loading or the device being physical 38 75 * unplugged (e.g. a USB network device). 39 76 * … … 49 86 * 50 87 * 51 * @subsection subsec_netflt_msc_dis_rel Disconnect from the network and release 88 * @subsection subsec_netflt_msc_dis_rel Disconnect from the network and release - OBSOLETE 52 89 * 53 90 * @msc … … 114 151 * 115 152 * 116 * @subsection subsec_netflt_msc_hif_rm Host Interface Removal 153 * @subsection subsec_netflt_msc_hif_rm Host Interface Removal - OBSOLETE 117 154 * 118 155 * The ifnet_t (pIf) is a tricky customer as any reference to it can potentially … … 154 191 * 155 192 * 156 * @subsection subsec_netflt_msc_hif_rm Host Interface Rediscovery 193 * @subsection subsec_netflt_msc_hif_rm Host Interface Rediscovery - OBSOLETE 157 194 * 158 195 * The rediscovery is performed when we receive a send request and a certain … … 338 375 static bool vboxNetFltMaybeRediscovered(PVBOXNETFLTINS pThis) 339 376 { 340 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; 341 uint64_t Now = RTTimeNanoTS(); 342 bool fRediscovered; 343 bool fDoIt; 377 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; 378 uint64_t Now; 379 bool fRediscovered; 380 bool fDoIt; 381 382 /* 383 * Don't do rediscovery if we're called with preemption disabled. 384 * 385 * Note! This may cause trouble if we're always called with preemptioni 386 * disabled and vboxNetFltOsMaybeRediscovered actually does some real 387 * work. For the time being though, only Darwin and FreeBSD depends 388 * on these call outs and neither supports sending with preemption 389 * disabled. 390 */ 391 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD)) 392 return false; 344 393 345 394 /* 346 395 * Rediscovered already? Time to try again? 347 396 */ 348 RTSpinlockAcquire(pThis->hSpinlock, &Tmp); 397 Now = RTTimeNanoTS(); 398 RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp); 349 399 350 400 fRediscovered = !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost); … … 355 405 ASMAtomicWriteBool(&pThis->fRediscoveryPending, true); 356 406 357 RTSpinlockRelease (pThis->hSpinlock, &Tmp);407 RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp); 358 408 359 409 /* … … 372 422 373 423 if (fRediscovered) 374 vboxNetFltPortOsSetActive(pThis, pThis->fActive); 424 /** @todo this isn't 100% serialized. */ 425 vboxNetFltPortOsSetActive(pThis, pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE); 375 426 } 376 427 … … 394 445 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); 395 446 AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, VERR_INVALID_STATE); 396 Assert(pThis->fActive);397 447 398 448 /* … … 400 450 * before invoking the OS specific code. 401 451 */ 402 vboxNetFltRetain(pThis, true /* fBusy */); 403 if ( !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost) 404 || vboxNetFltMaybeRediscovered(pThis)) 405 rc = vboxNetFltPortOsXmit(pThis, pSG, fDst); 406 vboxNetFltRelease(pThis, true /* fBusy */); 452 if (RT_LIKELY(vboxNetFltTryRetainBusyActive(pThis))) 453 { 454 if ( !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost) 455 || vboxNetFltMaybeRediscovered(pThis)) 456 rc = vboxNetFltPortOsXmit(pThis, pSG, fDst); 457 vboxNetFltRelease(pThis, true /* fBusy */); 458 } 407 459 408 460 return rc; … … 424 476 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); 425 477 AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, VERR_INVALID_STATE); 426 AssertReturn( !pThis->fActive, VERR_INVALID_STATE);478 AssertReturn(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING, VERR_INVALID_STATE); 427 479 428 480 /* … … 442 494 443 495 /** 444 * @copydoc INTNETTRUNKIFPORT::pfnSetActive 445 */ 446 static DECLCALLBACK(bool) vboxNetFltPortSetActive(PINTNETTRUNKIFPORT pIfPort, bool fActive) 447 { 448 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort); 496 * @copydoc INTNETTRUNKIFPORT::pfnSetState 497 */ 498 static DECLCALLBACK(INTNETTRUNKIFSTATE) vboxNetFltPortSetState(PINTNETTRUNKIFPORT pIfPort, INTNETTRUNKIFSTATE enmState) 499 { 500 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort); 501 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; 502 INTNETTRUNKIFSTATE enmOldTrunkState; 449 503 450 504 /* … … 454 508 AssertPtr(pThis->pGlobals); 455 509 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); 456 AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, false); 457 458 /* 459 * We're assuming that the caller is serializing the calls, so we don't 460 * have to be extremely careful here. Just update first and then call 461 * the OS specific code, the update must be serialized for various reasons. 462 */ 463 if (ASMAtomicReadBool(&pThis->fActive) != fActive) 464 { 465 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; 466 RTSpinlockAcquire(pThis->hSpinlock, &Tmp); 467 ASMAtomicWriteBool(&pThis->fActive, fActive); 468 RTSpinlockRelease(pThis->hSpinlock, &Tmp); 469 470 vboxNetFltPortOsSetActive(pThis, fActive); 471 } 472 else 473 fActive = !fActive; 474 return !fActive; 510 AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, INTNETTRUNKIFSTATE_INVALID); 511 AssertReturn(enmState > INTNETTRUNKIFSTATE_INVALID && enmState < INTNETTRUNKIFSTATE_END, 512 INTNETTRUNKIFSTATE_INVALID); 513 514 /* 515 * Take the lock and change the state. 516 */ 517 RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp); 518 enmOldTrunkState = pThis->enmTrunkState; 519 if (enmOldTrunkState != enmState) 520 ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmTrunkState, enmState); 521 RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp); 522 523 /* 524 * If the state change indicates that the trunk has become active or 525 * inactive, call the OS specific part so they can work the promiscuous 526 * settings and such. 527 * Note! The caller makes sure there are no concurrent pfnSetState calls. 528 */ 529 if ((enmOldTrunkState == INTNETTRUNKIFSTATE_ACTIVE) != (enmState == INTNETTRUNKIFSTATE_ACTIVE)) 530 vboxNetFltPortOsSetActive(pThis, (enmState == INTNETTRUNKIFSTATE_ACTIVE)); 531 532 return enmOldTrunkState; 475 533 } 476 534 … … 496 554 497 555 Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected); 498 Assert( !pThis->fActive);556 Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING); 499 557 Assert(!pThis->fRediscoveryPending); 500 558 Assert(!pThis->cBusy); … … 503 561 * Disconnect and release it. 504 562 */ 505 RTSpinlockAcquire (pThis->hSpinlock, &Tmp);563 RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp); 506 564 vboxNetFltSetState(pThis, kVBoxNetFltInsState_Disconnecting); 507 RTSpinlockRelease (pThis->hSpinlock, &Tmp);565 RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp); 508 566 509 567 vboxNetFltOsDisconnectIt(pThis); … … 511 569 512 570 #ifdef VBOXNETFLT_STATIC_CONFIG 513 RTSpinlockAcquire (pThis->hSpinlock, &Tmp);571 RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp); 514 572 vboxNetFltSetState(pThis, kVBoxNetFltInsState_Unconnected); 515 RTSpinlockRelease (pThis->hSpinlock, &Tmp);573 RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp); 516 574 #endif 517 575 … … 543 601 Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Disconnecting); 544 602 #endif 545 Assert( !pThis->fActive);603 Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING); 546 604 Assert(!pThis->fRediscoveryPending); 547 605 Assert(!pThis->cRefs); … … 684 742 685 743 /** 744 * Tries to retain the device as busy if the trunk is active. 745 * 746 * This is used before calling pfnRecv or pfnPreRecv. 747 * 748 * @returns true if we succeeded in retaining a busy reference to the active 749 * device. false if we failed. 750 * @param pThis The instance. 751 */ 752 DECLHIDDEN(bool) vboxNetFltTryRetainBusyActive(PVBOXNETFLTINS pThis) 753 { 754 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; 755 uint32_t cRefs; 756 bool fRc; 757 758 /* 759 * Paranoid Android. 760 */ 761 AssertPtr(pThis); 762 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); 763 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION); 764 Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid 765 && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed); 766 AssertPtr(pThis->pGlobals); 767 Assert(pThis->hEventIdle != NIL_RTSEMEVENT); 768 Assert(pThis->hSpinlock != NIL_RTSPINLOCK); 769 Assert(pThis->szName[0]); 770 771 /* 772 * Do the retaining and checking behind the spinlock. 773 */ 774 RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp); 775 fRc = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE; 776 if (fRc) 777 { 778 cRefs = ASMAtomicIncU32(&pThis->cRefs); 779 AssertMsg(cRefs > 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs); 780 781 cRefs = ASMAtomicIncU32(&pThis->cBusy); 782 AssertMsg(cRefs >= 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs); 783 } 784 RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp); 785 786 return fRc; 787 } 788 789 790 /** 791 * Tries to retain the device as busy if the trunk is not disconnecting. 792 * 793 * This is used before reporting stuff to the internal network. 794 * 795 * @returns true if we succeeded in retaining a busy reference to the active 796 * device. false if we failed. 797 * @param pThis The instance. 798 */ 799 DECLHIDDEN(bool) vboxNetFltTryRetainBusyNotDisconnected(PVBOXNETFLTINS pThis) 800 { 801 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; 802 uint32_t cRefs; 803 bool fRc; 804 805 /* 806 * Paranoid Android. 807 */ 808 AssertPtr(pThis); 809 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); 810 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION); 811 Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid 812 && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed); 813 AssertPtr(pThis->pGlobals); 814 Assert(pThis->hEventIdle != NIL_RTSEMEVENT); 815 Assert(pThis->hSpinlock != NIL_RTSPINLOCK); 816 Assert(pThis->szName[0]); 817 818 /* 819 * Do the retaining and checking behind the spinlock. 820 */ 821 RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp); 822 fRc = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE 823 || pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE; 824 if (fRc) 825 { 826 cRefs = ASMAtomicIncU32(&pThis->cRefs); 827 AssertMsg(cRefs > 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs); 828 829 cRefs = ASMAtomicIncU32(&pThis->cBusy); 830 AssertMsg(cRefs >= 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs); 831 } 832 RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp); 833 834 return fRc; 835 } 836 837 838 /** 686 839 * @copydoc INTNETTRUNKIFPORT::pfnRetain 687 840 */ … … 713 866 * Validate state. 714 867 */ 715 Assert( !pThis->fActive);868 Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE); 716 869 Assert(!pThis->fRediscoveryPending); 717 870 Assert(!pThis->cBusy); … … 736 889 pThis->pSwitchPort = NULL; 737 890 738 Assert( !pThis->fActive);891 Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE); 739 892 return rc; 740 893 } … … 773 926 pNew->MyPort.pfnRelease = vboxNetFltPortRelease; 774 927 pNew->MyPort.pfnDisconnectAndRelease= vboxNetFltPortDisconnectAndRelease; 775 pNew->MyPort.pfnSet Active = vboxNetFltPortSetActive;928 pNew->MyPort.pfnSetState = vboxNetFltPortSetState; 776 929 pNew->MyPort.pfnWaitForIdle = vboxNetFltPortWaitForIdle; 777 930 pNew->MyPort.pfnXmit = vboxNetFltPortXmit; … … 781 934 pNew->hSpinlock = NIL_RTSPINLOCK; 782 935 pNew->enmState = kVBoxNetFltInsState_Initializing; 783 pNew-> fActive = false;936 pNew->enmTrunkState = INTNETTRUNKIFSTATE_INACTIVE; 784 937 pNew->fDisconnectedFromHost = false; 785 938 pNew->fRediscoveryPending = false;
Note:
See TracChangeset
for help on using the changeset viewer.