VirtualBox

Ignore:
Timestamp:
Oct 28, 2009 2:46:59 AM (15 years ago)
Author:
vboxsync
Message:

VBoxNetFlt/FreeBSD: Fix locking problems. Contributed by Fredrik Lindberg

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/HostDrivers/VBoxNetFlt/freebsd/VBoxNetFlt-freebsd.c

    r23526 r24123  
    4444#include <sys/sockio.h>
    4545#include <sys/syscallsubr.h>
     46#include <sys/queue.h>
     47#include <sys/taskqueue.h>
    4648
    4749#include <net/if.h>
     
    7981static ng_disconnect_t    ng_vboxnetflt_disconnect;
    8082static int        ng_vboxnetflt_mod_event(module_t mod, int event, void *data);
    81 static int        ng_vboxnetflt_rcv_in(hook_p node, item_p item);
    82 static int        ng_vboxnetflt_rcv_out(hook_p node, item_p item);
    8383
    8484/** Netgraph node type */
     
    113113    .version =    NG_ABI_VERSION,
    114114    .name =        NG_VBOXNETFLT_NODE_TYPE,
    115     .mod_event =    vboxnetflt_modevent,   
    116     .constructor =    ng_vboxnetflt_constructor,   
     115    .mod_event =    vboxnetflt_modevent,
     116    .constructor =    ng_vboxnetflt_constructor,
    117117    .rcvmsg =     ng_vboxnetflt_rcvmsg,
    118118    .shutdown =    ng_vboxnetflt_shutdown,
     
    268268    {
    269269#if __FreeBSD_version >= 800000
    270         NG_HOOK_SET_RCVDATA(hook, ng_vboxnetflt_rcv_in);
    271270        NG_HOOK_SET_TO_INBOUND(hook);
    272271#endif
     
    275274    else if (strcmp(name, NG_VBOXNETFLT_HOOK_OUT) == 0)
    276275    {
    277 #if __FreeBSD_version >= 800000
    278         NG_HOOK_SET_RCVDATA(hook, ng_vboxnetflt_rcv_out);
    279 #endif
    280276        pThis->u.s.output = hook;
    281277    }
     
    311307/**
    312308 * Handle data on netgraph hooks.
     309 * Frames processing is deferred to a taskqueue because this might
     310 * be called with non-sleepable locks held and code paths inside
     311 * the virtual switch might sleep.
    313312 */
    314313static int ng_vboxnetflt_rcvdata(hook_p hook, item_p item)
    315314{
    316     const node_p node = NG_HOOK_NODE(hook);
    317     PVBOXNETFLTINS pThis = NG_NODE_PRIVATE(node);
    318     struct mbuf *m;
    319 
    320     if (pThis->u.s.input == hook)
    321         return ng_vboxnetflt_rcv_in(hook, item);
    322     else if (pThis->u.s.output == hook)
    323         return ng_vboxnetflt_rcv_out(hook, item);
    324     else
    325     {
    326         NGI_GET_M(item, m);
    327         NG_FREE_ITEM(item);
    328     }
    329     return (0);
    330 }
    331 
    332 /**
    333  * Handle incoming hook. This is connected to the
    334  * input path of the interface, thus handling incoming frames.
    335  */
    336 static int ng_vboxnetflt_rcv_in(hook_p hook, item_p item)
    337 {
    338     struct mbuf *m, *m0;
    339     struct m_tag *mtag;
    340315    const node_p node = NG_HOOK_NODE(hook);
    341316    PVBOXNETFLTINS pThis = NG_NODE_PRIVATE(node);
    342317    struct ifnet *ifp = pThis->u.s.ifp;
    343     bool fActive, fDropIt = false;
    344     unsigned int cSegs = 0;
    345     PINTNETSG pSG;
     318    struct mbuf *m;
     319    struct m_tag *mtag;
     320    bool fActive;
     321
     322    fActive = ASMAtomicUoReadBool(&pThis->fActive);
    346323
    347324    NGI_GET_M(item, m);
    348325    NG_FREE_ITEM(item);
    349326
    350     fActive = ASMAtomicUoReadBool(&pThis->fActive);
    351     if (!fActive)
    352         goto out;
    353 
     327    /* Locate tag to see if processing should be skipped for this frame */
    354328    mtag = m_tag_locate(m, MTAG_VBOX, PACKET_TAG_VBOX, NULL);
    355329    if (mtag != NULL)
     
    357331        m_tag_unlink(m, mtag);
    358332        m_tag_free(mtag);
    359         goto out;
    360     }
    361     vboxNetFltRetain(pThis, true /* fBusy */);
    362 
    363     for (m0 = m; m0 != NULL; m0 = m0->m_next)
    364     {
    365         if (m0->m_len > 0)
    366             cSegs++;
    367     }
    368 
    369 #ifdef PADD_RUNT_FRAMES_FROM_HOST
    370     if (m_length(m, NULL) < 60)
    371         cSegs++;
    372 #endif
    373 
    374     /* Create a copy of the mbuf and hand it to the virtual switch */
    375     pSG = RTMemTmpAlloc(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
    376     vboxNetFltFreeBSDMBufToSG(pThis, m, pSG, cSegs, 0);
    377     fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, INTNETTRUNKDIR_WIRE);
    378     RTMemTmpFree(pSG);
    379     vboxNetFltRelease(pThis, true /* fBusy */);
    380 
    381 out:
    382     /* Only deliver it to the host stack if the destination weren't a guest */
    383     if (fDropIt)
     333    }
     334
     335    /*
     336     * Handle incoming hook. This is connected to the
     337     * input path of the interface, thus handling incoming frames.
     338     */
     339    if (pThis->u.s.input == hook)
     340    {
     341        if (mtag != NULL || !fActive)
     342        {
     343            ether_demux(ifp, m);
     344            return (0);
     345        }
     346        mtx_lock_spin(&pThis->u.s.inq.ifq_mtx);
     347        _IF_ENQUEUE(&pThis->u.s.inq, m);
     348        mtx_unlock_spin(&pThis->u.s.inq.ifq_mtx);
     349        taskqueue_enqueue_fast(taskqueue_fast, &pThis->u.s.tskin);
     350    }
     351    /**
     352     * Handle mbufs on the outgoing hook, frames going to the interface
     353     */
     354    else if (pThis->u.s.output == hook)
     355    {
     356        if (mtag != NULL || !fActive)
     357            return ether_output_frame(ifp, m);
     358        mtx_lock_spin(&pThis->u.s.outq.ifq_mtx);
     359        _IF_ENQUEUE(&pThis->u.s.outq, m);
     360        mtx_unlock_spin(&pThis->u.s.outq.ifq_mtx);
     361        taskqueue_enqueue_fast(taskqueue_fast, &pThis->u.s.tskout);
     362    }
     363    else
    384364    {
    385365        m_freem(m);
    386         return (0);
    387     }
    388     ether_demux(ifp, m);
     366    }
    389367    return (0);
    390 }
    391 
    392 /**
    393  * Handle mbufs on the outgoing hook, frames going to the interface
    394  */
    395 static int ng_vboxnetflt_rcv_out(hook_p hook, item_p item)
    396 {
    397     struct mbuf *m, *m0;
    398     struct m_tag *mtag;
    399     const node_p node = NG_HOOK_NODE(hook);
    400     PVBOXNETFLTINS pThis = NG_NODE_PRIVATE(node);
    401     struct ifnet *ifp = pThis->u.s.ifp;
    402     unsigned int cSegs = 0;
    403     bool fDropIt = false, fActive;
    404     PINTNETSG pSG;
    405 
    406     NGI_GET_M(item, m);
    407     NG_FREE_ITEM(item);
    408 
    409     fActive = ASMAtomicUoReadBool(&pThis->fActive);
    410     if (!fActive)
    411         return ether_output_frame(ifp, m);
    412 
    413     vboxNetFltRetain(pThis, true /* fBusy */);
    414     /* Pass directly to interface if the packet originated from us */
    415     mtag = m_tag_locate(m, MTAG_VBOX, PACKET_TAG_VBOX, NULL);
    416     if (mtag != NULL)
    417     {
    418         m_tag_unlink(m, mtag);
    419         m_tag_free(mtag);
    420         goto out;
    421     }
    422 
    423     for (m0 = m; m0 != NULL; m0 = m0->m_next)
    424     {
    425         if (m0->m_len > 0)
    426             cSegs++;
    427     }
    428 
    429 #ifdef PADD_RUNT_FRAMES_FROM_HOST
    430     if (m_length(m, NULL) < 60)
    431         cSegs++;
    432 #endif
    433     /* Create a copy and deliver to the virtual switch */
    434     pSG = RTMemTmpAlloc(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
    435     vboxNetFltFreeBSDMBufToSG(pThis, m, pSG, cSegs, 0);
    436     fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, INTNETTRUNKDIR_HOST);
    437     RTMemTmpFree(pSG);
    438 
    439 out:
    440     vboxNetFltRelease(pThis, true /* fBusy */);
    441     if (fDropIt)
    442     {
    443         m_freem(m);
    444         return (0);
    445     }
    446 
    447     return ether_output_frame(ifp, m);
    448368}
    449369
     
    464384{
    465385    return (0);
     386}
     387
     388/**
     389 * Input processing task, handles incoming frames
     390 */
     391static void vboxNetFltFreeBSDinput(void *arg, int pending)
     392{
     393    PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)arg;
     394    struct mbuf *m, *m0;
     395    struct ifnet *ifp = pThis->u.s.ifp;
     396    unsigned int cSegs = 0;
     397    bool fDropIt = false, fActive;
     398    PINTNETSG pSG;
     399
     400    vboxNetFltRetain(pThis, true /* fBusy */);
     401    for (;;)
     402    {
     403        mtx_lock_spin(&pThis->u.s.inq.ifq_mtx);
     404        _IF_DEQUEUE(&pThis->u.s.inq, m);
     405        mtx_unlock_spin(&pThis->u.s.inq.ifq_mtx);
     406        if (m == NULL)
     407            break;
     408
     409        for (m0 = m; m0 != NULL; m0 = m0->m_next)
     410            if (m0->m_len > 0)
     411                cSegs++;
     412
     413#ifdef PADD_RUNT_FRAMES_FROM_HOST
     414        if (m_length(m, NULL) < 60)
     415            cSegs++;
     416#endif
     417
     418        /* Create a copy and deliver to the virtual switch */
     419        pSG = RTMemTmpAlloc(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
     420        vboxNetFltFreeBSDMBufToSG(pThis, m, pSG, cSegs, 0);
     421        fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, INTNETTRUNKDIR_HOST);
     422        RTMemTmpFree(pSG);
     423        if (fDropIt)
     424            m_freem(m);
     425        else
     426            ether_demux(ifp, m);
     427    }
     428    vboxNetFltRelease(pThis, true /* fBusy */);
     429}
     430
     431/**
     432 * Output processing task, handles outgoing frames
     433 */
     434static void vboxNetFltFreeBSDoutput(void *arg, int pending)
     435{
     436    PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)arg;
     437    struct mbuf *m, *m0;
     438    struct ifnet *ifp = pThis->u.s.ifp;
     439    unsigned int cSegs = 0;
     440    bool fDropIt = false, fActive;
     441    PINTNETSG pSG;
     442
     443    vboxNetFltRetain(pThis, true /* fBusy */);
     444    for (;;)
     445    {
     446        mtx_lock_spin(&pThis->u.s.outq.ifq_mtx);
     447        _IF_DEQUEUE(&pThis->u.s.outq, m);
     448        mtx_unlock_spin(&pThis->u.s.outq.ifq_mtx);
     449        if (m == NULL)
     450            break;
     451
     452        for (m0 = m; m0 != NULL; m0 = m0->m_next)
     453            if (m0->m_len > 0)
     454                cSegs++;
     455
     456#ifdef PADD_RUNT_FRAMES_FROM_HOST
     457        if (m_length(m, NULL) < 60)
     458            cSegs++;
     459#endif
     460        /* Create a copy and deliver to the virtual switch */
     461        pSG = RTMemTmpAlloc(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
     462        vboxNetFltFreeBSDMBufToSG(pThis, m, pSG, cSegs, 0);
     463        fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, INTNETTRUNKDIR_HOST);
     464        RTMemTmpFree(pSG);
     465
     466        if (fDropIt)
     467            m_freem(m);
     468        else
     469            ether_output_frame(ifp, m);
     470    }
     471    vboxNetFltRelease(pThis, true /* fBusy */);
    466472}
    467473
     
    537543    /* Create a new netgraph node for this instance */
    538544    if (ng_make_node_common(&ng_vboxnetflt_typestruct, &node) != 0)
    539         return VERR_INTERNAL_ERROR;       
     545        return VERR_INTERNAL_ERROR;
    540546
    541547    RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
     
    544550    bcopy(IF_LLADDR(ifp), &pThis->u.s.Mac, ETHER_ADDR_LEN);
    545551    ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
     552    /* Initialize deferred input queue */
     553    bzero(&pThis->u.s.inq, sizeof(struct ifqueue));
     554    mtx_init(&pThis->u.s.inq.ifq_mtx, "vboxnetflt inq", NULL, MTX_SPIN);
     555    TASK_INIT(&pThis->u.s.tskin, 0, vboxNetFltFreeBSDinput, pThis);
     556
     557    /* Initialize deferred output queue */
     558    bzero(&pThis->u.s.outq, sizeof(struct ifqueue));
     559    mtx_init(&pThis->u.s.outq.ifq_mtx, "vboxnetflt outq", NULL, MTX_SPIN);
     560    TASK_INIT(&pThis->u.s.tskout, 0, vboxNetFltFreeBSDoutput, pThis);
     561
    546562    RTSpinlockRelease(pThis->hSpinlock, &Tmp);
    547563
     
    572588
    573589    if (ifp0 != NULL)
     590    {
     591        vboxNetFltOsDeleteInstance(pThis);
    574592        vboxNetFltOsInitInstance(pThis, NULL);
     593    }
    575594
    576595    return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
     
    579598void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
    580599{
     600
     601    taskqueue_drain(taskqueue_fast, &pThis->u.s.tskin);
     602    taskqueue_drain(taskqueue_fast, &pThis->u.s.tskout);
     603
     604    mtx_destroy(&pThis->u.s.inq.ifq_mtx);
     605    mtx_destroy(&pThis->u.s.outq.ifq_mtx);
    581606
    582607    if (pThis->u.s.node != NULL)
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette