VirtualBox

Changeset 33325 in vbox


Ignore:
Timestamp:
Oct 21, 2010 8:34:14 PM (14 years ago)
Author:
vboxsync
Message:

virtio-net: large (GSO) receive packet support (#4807)

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/VBox/pdmnetifs.h

    r28800 r33325  
    138138
    139139    /**
     140     * Receive data with segmentation context from the network.
     141     *
     142     * @returns VBox status code.
     143     * @param   pInterface      Pointer to the interface structure containing the called function pointer.
     144     * @param   pvBuf           The available data.
     145     * @param   cb              Number of bytes available in the buffer.
     146     * @param   pGso            Segmentation context.
     147     *
     148     * @thread  Non-EMT.
     149     */
     150    DECLR3CALLBACKMEMBER(int, pfnReceiveGso,(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb, PCPDMNETWORKGSO pGso));
     151
     152    /**
    140153     * Do pending transmit work on the leaf driver's XMIT thread.
    141154     *
  • trunk/src/VBox/Devices/Network/DevVirtioNet.cpp

    r31767 r33325  
    1919#define LOG_GROUP LOG_GROUP_DEV_VIRTIO_NET
    2020#define VNET_GC_SUPPORT
    21 //#define VNET_WITH_GSO
     21#define VNET_WITH_GSO
     22#define VNET_WITH_MERGEABLE_RX_BUFS
    2223
    2324#include <VBox/pdmdev.h>
     
    228229AssertCompileSize(VNETHDR, 10);
    229230
     231struct VNetHdrMrx
     232{
     233    VNETHDR  Hdr;
     234    uint16_t u16NumBufs;
     235};
     236typedef struct VNetHdrMrx VNETHDRMRX;
     237typedef VNETHDRMRX *PVNETHDRMRX;
     238AssertCompileSize(VNETHDRMRX, 12);
     239
    230240AssertCompileMemberOffset(VNETSTATE, VPCI, 0);
    231241
     
    255265AssertCompileSize(VNETCTLHDR, 2);
    256266
     267/* Returns true if large packets are written into several RX buffers. */
     268DECLINLINE(bool) vnetMergeableRxBuffers(PVNETSTATE pState)
     269{
     270    return !!(pState->VPCI.uGuestFeatures & VNET_F_MRG_RXBUF);
     271}
     272
    257273DECLINLINE(int) vnetCsEnter(PVNETSTATE pState, int rcBusy)
    258274{
     
    292308    Log(("%s %s packet #%d (%d bytes):\n",
    293309         INSTANCE(pState), cszText, ++pState->u32PktNo, cb));
    294     //Log3(("%.*Rhxd\n", cb, cpPacket));
     310    Log3(("%.*Rhxd\n", cb, cpPacket));
    295311#endif
    296312}
     
    319335        | VNET_F_HOST_UFO
    320336#endif
     337#ifdef VNET_WITH_MERGEABLE_RX_BUFS
     338        | VNET_F_MRG_RXBUF
     339#endif
    321340        ;
    322341}
     
    385404    else
    386405        STATUS = 0;
     406
    387407    /*
    388408     * By default we pass all packets up since the older guests cannot control
     
    655675    if (   u16Ptr[6] == RT_H2BE_U16(0x8100)
    656676        && !ASMBitTest(pState->aVlanFilter, RT_BE2H_U16(u16Ptr[7]) & 0xFFF))
     677    {
     678        Log4(("%s vnetAddressFilter: not our VLAN, returning false\n", INSTANCE(pState)));
    657679        return false;
     680    }
    658681
    659682    if (vnetIsBroadcast(pvBuf))
     
    665688    if (!memcmp(pState->config.mac.au8, pvBuf, sizeof(RTMAC)))
    666689        return true;
     690    Log4(("%s vnetAddressFilter: %RTmac (conf) != %RTmac (dest)\n",
     691          INSTANCE(pState), pState->config.mac.au8, pvBuf));
    667692
    668693    for (unsigned i = 0; i < pState->nMacFilterEntries; i++)
    669694        if (!memcmp(&pState->aMacFilter[i], pvBuf, sizeof(RTMAC)))
    670695            return true;
     696
     697    Log2(("%s vnetAddressFilter: failed all tests, returning false, packet dump follows:\n", INSTANCE(pState)));
     698    vnetPacketDump(pState, (const uint8_t*)pvBuf, cb, "<-- Incoming");
    671699
    672700    return false;
     
    685713 * @thread  RX
    686714 */
    687 static int vnetHandleRxPacket(PVNETSTATE pState, const void *pvBuf, size_t cb)
    688 {
    689     VNETHDR hdr;
    690 
    691     hdr.u8Flags   = 0;
    692     hdr.u8GSOType = VNETHDR_GSO_NONE;
    693 
    694     vnetPacketDump(pState, (const uint8_t*)pvBuf, cb, "<-- Incoming");
     715static int vnetHandleRxPacket(PVNETSTATE pState, const void *pvBuf, size_t cb,
     716                              PCPDMNETWORKGSO pGso)
     717{
     718    VNETHDRMRX   Hdr;
     719    PVNETHDRMRX pHdr;
     720    unsigned    uHdrLen;
     721    RTGCPHYS     addrHdrMrx = 0;
     722
     723    if (pGso)
     724    {
     725        Log2(("%s vnetHandleRxPacket: gso type=%x cbHdr=%u mss=%u"
     726              " off1=0x%x off2=0x%x\n", INSTANCE(pState), pGso->u8Type,
     727              pGso->cbHdrs, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
     728        Hdr.Hdr.u8Flags = VNETHDR_F_NEEDS_CSUM;
     729        switch (pGso->u8Type)
     730        {
     731            case PDMNETWORKGSOTYPE_IPV4_TCP:
     732                Hdr.Hdr.u8GSOType = VNETHDR_GSO_TCPV4;
     733                Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
     734                break;
     735            case PDMNETWORKGSOTYPE_IPV6_TCP:
     736                Hdr.Hdr.u8GSOType = VNETHDR_GSO_TCPV6;
     737                Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
     738                break;
     739            case PDMNETWORKGSOTYPE_IPV4_UDP:
     740                Hdr.Hdr.u8GSOType = VNETHDR_GSO_UDP;
     741                Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
     742                break;
     743            default:
     744                return VERR_INVALID_PARAMETER;
     745        }
     746        Hdr.Hdr.u16HdrLen = pGso->cbHdrs;
     747        Hdr.Hdr.u16GSOSize = pGso->cbMaxSeg;
     748        Hdr.Hdr.u16CSumStart = pGso->offHdr2;
     749        STAM_REL_COUNTER_INC(&pState->StatReceiveGSO);
     750    }
     751    else
     752    {
     753        Hdr.Hdr.u8Flags   = 0;
     754        Hdr.Hdr.u8GSOType = VNETHDR_GSO_NONE;
     755    }
     756
     757    if (vnetMergeableRxBuffers(pState))
     758        uHdrLen = sizeof(VNETHDRMRX);
     759    else
     760        uHdrLen = sizeof(VNETHDR);
     761
     762    //vnetPacketDump(pState, (const uint8_t*)pvBuf, cb, "<-- Incoming");
    695763
    696764    unsigned int uOffset = 0;
    697     for (unsigned int nElem = 0; uOffset < cb; nElem++)
     765    unsigned int nElem;
     766    for (nElem = 0; uOffset < cb; nElem++)
    698767    {
    699768        VQUEUEELEM elem;
    700         unsigned int nSeg = 0, uElemSize = 0;
     769        unsigned int nSeg = 0, uElemSize = 0, cbReserved = 0;
    701770
    702771        if (!vqueueGet(&pState->VPCI, pState->pRxQueue, &elem))
    703772        {
     773            /*
     774             * @todo: It is possible to run out of RX buffers if only a few
     775             * were added and we received a big packet.
     776             */
    704777            Log(("%s vnetHandleRxPacket: Suddenly there is no space in receive queue!\n", INSTANCE(pState)));
    705778            return VERR_INTERNAL_ERROR;
     
    714787        if (nElem == 0)
    715788        {
    716             /* The very first segment of the very first element gets the header. */
    717             if (elem.aSegsIn[nSeg].cb != sizeof(VNETHDR))
     789            if (vnetMergeableRxBuffers(pState))
    718790            {
    719                 Log(("%s vnetHandleRxPacket: The first descriptor does match the header size!\n", INSTANCE(pState)));
    720                 return VERR_INTERNAL_ERROR;
     791                addrHdrMrx = elem.aSegsIn[nSeg].addr;
     792                cbReserved = uHdrLen;
    721793            }
    722 
    723             elem.aSegsIn[nSeg++].pv = &hdr;
    724             uElemSize += sizeof(VNETHDR);
     794            else
     795            {
     796                /* The very first segment of the very first element gets the header. */
     797                if (elem.aSegsIn[nSeg].cb != sizeof(VNETHDR))
     798                {
     799                    Log(("%s vnetHandleRxPacket: The first descriptor does match the header size!\n", INSTANCE(pState)));
     800                    return VERR_INTERNAL_ERROR;
     801                }
     802                elem.aSegsIn[nSeg++].pv = &Hdr;
     803            }
     804            uElemSize += uHdrLen;
    725805        }
    726 
    727806        while (nSeg < elem.nIn && uOffset < cb)
    728807        {
    729             unsigned int uSize = (unsigned int)RT_MIN(elem.aSegsIn[nSeg].cb, cb - uOffset);
     808            unsigned int uSize = (unsigned int)RT_MIN(elem.aSegsIn[nSeg].cb - (nSeg?0:cbReserved),
     809                                                      cb - uOffset);
    730810            elem.aSegsIn[nSeg++].pv = (uint8_t*)pvBuf + uOffset;
    731811            uOffset += uSize;
     
    733813        }
    734814        STAM_PROFILE_START(&pState->StatReceiveStore, a);
    735         vqueuePut(&pState->VPCI, pState->pRxQueue, &elem, uElemSize);
     815        vqueuePut(&pState->VPCI, pState->pRxQueue, &elem, uElemSize, cbReserved);
    736816        STAM_PROFILE_STOP(&pState->StatReceiveStore, a);
     817        if (!vnetMergeableRxBuffers(pState))
     818            break;
     819        cbReserved = 0;
     820    }
     821    if (vnetMergeableRxBuffers(pState))
     822    {
     823        Hdr.u16NumBufs = nElem;
     824        int rc = PDMDevHlpPhysWrite(pState->VPCI.CTX_SUFF(pDevIns), addrHdrMrx,
     825                                    &Hdr, sizeof(Hdr));
     826        if (RT_FAILURE(rc))
     827        {
     828            Log(("%s vnetHandleRxPacket: Failed to write merged RX buf header: %Rrc\n",
     829                 INSTANCE(pState), rc));
     830            return rc;
     831        }
    737832    }
    738833    vqueueSync(&pState->VPCI, pState->pRxQueue);
     834    if (uOffset < cb)
     835    {
     836        Log(("%s vnetHandleRxPacket: Packet did not fit into RX queue (packet size=%u)!\n",
     837             INSTANCE(pState), cb));
     838        return VERR_TOO_MUCH_DATA;
     839    }
    739840
    740841    return VINF_SUCCESS;
     
    742843
    743844/**
    744  * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
    745  */
    746 static DECLCALLBACK(int) vnetNetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
     845 * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
     846 */
     847static DECLCALLBACK(int) vnetNetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface,
     848                                                    const void *pvBuf, size_t cb,
     849                                                    PCPDMNETWORKGSO pGso)
    747850{
    748851    VNETSTATE *pState = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkDown);
    749852
    750     Log2(("%s vnetNetworkDown_Receive: pvBuf=%p cb=%u\n", INSTANCE(pState), pvBuf, cb));
     853    Log2(("%s vnetNetworkDown_ReceiveGso: pvBuf=%p cb=%u pGso=%p\n",
     854          INSTANCE(pState), pvBuf, cb, pGso));
    751855    int rc = vnetCanReceive(pState);
    752856    if (RT_FAILURE(rc))
     
    767871        if (RT_SUCCESS(rc))
    768872        {
    769             rc = vnetHandleRxPacket(pState, pvBuf, cb);
     873            rc = vnetHandleRxPacket(pState, pvBuf, cb, pGso);
    770874            STAM_REL_COUNTER_ADD(&pState->StatReceiveBytes, cb);
    771875            vnetCsRxLeave(pState);
     
    775879    STAM_PROFILE_STOP(&pState->StatReceive, a);
    776880    return rc;
     881}
     882
     883/**
     884 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
     885 */
     886static DECLCALLBACK(int) vnetNetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
     887{
     888    return vnetNetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
    777889}
    778890
     
    9411053    }
    9421054
     1055    unsigned int uHdrLen;
     1056    if (vnetMergeableRxBuffers(pState))
     1057        uHdrLen = sizeof(VNETHDRMRX);
     1058    else
     1059        uHdrLen = sizeof(VNETHDR);
     1060
    9431061    Log3(("%s vnetTransmitPendingPackets: About to trasmit %d pending packets\n", INSTANCE(pState),
    9441062          vringReadAvailIndex(&pState->VPCI, &pState->pTxQueue->VRing) - pState->pTxQueue->uNextAvailIndex));
     
    9501068    {
    9511069        unsigned int uOffset = 0;
    952         if (elem.nOut < 2 || elem.aSegsOut[0].cb != sizeof(VNETHDR))
     1070        if (elem.nOut < 2 || elem.aSegsOut[0].cb != uHdrLen)
    9531071        {
    9541072            Log(("%s vnetQueueTransmit: The first segment is not the header! (%u < 2 || %u != %u).\n",
    955                  INSTANCE(pState), elem.nOut, elem.aSegsOut[0].cb, sizeof(VNETHDR)));
     1073                 INSTANCE(pState), elem.nOut, elem.aSegsOut[0].cb, uHdrLen));
    9561074            break; /* For now we simply ignore the header, but it must be there anyway! */
    9571075        }
     
    9921110                    }
    9931111                    pSgBuf->cbUsed = uSize;
    994                     vnetPacketDump(pState, (uint8_t*)pSgBuf->aSegs[0].pvSeg, uSize, "--> Outgoing");
     1112                    //vnetPacketDump(pState, (uint8_t*)pSgBuf->aSegs[0].pvSeg, uSize, "--> Outgoing");
    9951113                    if (pGso)
    9961114                        STAM_REL_COUNTER_INC(&pState->StatTransmitGSO);
     
    14751593                          sizeof(pState->config.mac));
    14761594        AssertRCReturn(rc, rc);
     1595
    14771596        if (uVersion > VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1)
    14781597        {
     
    18101929    pState->INetworkDown.pfnWaitReceiveAvail = vnetNetworkDown_WaitReceiveAvail;
    18111930    pState->INetworkDown.pfnReceive          = vnetNetworkDown_Receive;
     1931    pState->INetworkDown.pfnReceiveGso       = vnetNetworkDown_ReceiveGso;
    18121932    pState->INetworkDown.pfnXmitPending      = vnetNetworkDown_XmitPending;
    18131933
  • trunk/src/VBox/Devices/Network/DrvIntNet.cpp

    r32167 r33325  
    726726                        /*
    727727                         * Generic segment offload frame (INTNETHDR_TYPE_GSO).
    728                          *
    729                          * This is where we do the offloading since we don't
    730                          * emulate any NICs with large receive offload (LRO).
    731728                         */
    732729                        STAM_COUNTER_INC(&pThis->StatReceivedGso);
     
    734731                        if (PDMNetGsoIsValid(pGso, cbFrame, cbFrame - sizeof(PDMNETWORKGSO)))
    735732                        {
    736                             cbFrame -= sizeof(PDMNETWORKGSO);
    737 
    738                             uint8_t         abHdrScratch[256];
    739                             uint32_t const  cSegs = PDMNetGsoCalcSegmentCount(pGso, cbFrame);
     733                            if (!pThis->pIAboveNet->pfnReceiveGso ||
     734                                RT_FAILURE(pThis->pIAboveNet->pfnReceiveGso(pThis->pIAboveNet,
     735                                                                            (uint8_t *)(pGso + 1),
     736                                                                            pHdr->cbFrame - sizeof(PDMNETWORKGSO),
     737                                                                            pGso)))
     738                            {
     739                                /*
     740                                 *
     741                                 * This is where we do the offloading since this NIC
     742                                 * does not support large receive offload (LRO).
     743                                 */
     744                                cbFrame -= sizeof(PDMNETWORKGSO);
     745
     746                                uint8_t         abHdrScratch[256];
     747                                uint32_t const  cSegs = PDMNetGsoCalcSegmentCount(pGso, cbFrame);
    740748#ifdef LOG_ENABLED
    741                             if (LogIsEnabled())
    742                             {
    743                                 uint64_t u64Now = RTTimeProgramNanoTS();
    744                                 LogFlow(("drvR3IntNetRecvRun: %-4d bytes at %llu ns  deltas: r=%llu t=%llu; GSO - %u segs\n",
    745                                          cbFrame, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS, cSegs));
    746                                 pThis->u64LastReceiveTS = u64Now;
    747                                 Log2(("drvR3IntNetRecvRun: cbFrame=%#x type=%d cbHdrs=%#x Hdr1=%#x Hdr2=%#x MMS=%#x\n"
    748                                       "%.*Rhxd\n",
    749                                       cbFrame, pGso->u8Type, pGso->cbHdrs, pGso->offHdr1, pGso->offHdr2, pGso->cbMaxSeg,
    750                                       cbFrame - sizeof(*pGso), pGso + 1));
    751                             }
     749                                if (LogIsEnabled())
     750                                {
     751                                    uint64_t u64Now = RTTimeProgramNanoTS();
     752                                    LogFlow(("drvR3IntNetRecvRun: %-4d bytes at %llu ns  deltas: r=%llu t=%llu; GSO - %u segs\n",
     753                                             cbFrame, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS, cSegs));
     754                                    pThis->u64LastReceiveTS = u64Now;
     755                                    Log2(("drvR3IntNetRecvRun: cbFrame=%#x type=%d cbHdrs=%#x Hdr1=%#x Hdr2=%#x MMS=%#x\n"
     756                                          "%.*Rhxd\n",
     757                                          cbFrame, pGso->u8Type, pGso->cbHdrs, pGso->offHdr1, pGso->offHdr2, pGso->cbMaxSeg,
     758                                          cbFrame - sizeof(*pGso), pGso + 1));
     759                                }
    752760#endif
    753                             for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
    754                             {
    755                                 uint32_t cbSegFrame;
    756                                 void    *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)(pGso + 1), cbFrame, abHdrScratch,
    757                                                                               iSeg, cSegs, &cbSegFrame);
    758                                 rc = drvR3IntNetRecvWaitForSpace(pThis);
    759                                 if (RT_FAILURE(rc))
     761                                for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
    760762                                {
    761                                     Log(("drvR3IntNetRecvRun: drvR3IntNetRecvWaitForSpace -> %Rrc; iSeg=%u cSegs=%u\n", iSeg, cSegs));
    762                                     break; /* we drop the rest. */
     763                                    uint32_t cbSegFrame;
     764                                    void    *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)(pGso + 1), cbFrame, abHdrScratch,
     765                                                                                  iSeg, cSegs, &cbSegFrame);
     766                                    rc = drvR3IntNetRecvWaitForSpace(pThis);
     767                                    if (RT_FAILURE(rc))
     768                                    {
     769                                        Log(("drvR3IntNetRecvRun: drvR3IntNetRecvWaitForSpace -> %Rrc; iSeg=%u cSegs=%u\n", iSeg, cSegs));
     770                                        break; /* we drop the rest. */
     771                                    }
     772                                    rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pvSegFrame, cbSegFrame);
     773                                    AssertRC(rc);
    763774                                }
    764                                 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pvSegFrame, cbSegFrame);
    765                                 AssertRC(rc);
    766775                            }
    767776                        }
  • trunk/src/VBox/Devices/VirtIO/Virtio.cpp

    r33314 r33325  
    181181}
    182182
    183 void vqueuePut(PVPCISTATE pState, PVQUEUE pQueue, PVQUEUEELEM pElem, uint32_t uLen)
    184 {
    185     unsigned int i, uOffset;
     183void vqueuePut(PVPCISTATE pState, PVQUEUE pQueue, PVQUEUEELEM pElem, uint32_t uLen, uint32_t uReserved)
     184{
     185    unsigned int i, uOffset, cbReserved = uReserved;
    186186
    187187    Log2(("%s vqueuePut: %s desc_idx=%u acb=%u\n", INSTANCE(pState),
    188188          QUEUENAME(pState, pQueue), pElem->uIndex, uLen));
    189     for (i = uOffset = 0; i < pElem->nIn && uOffset < uLen; i++)
    190     {
    191         uint32_t cbSegLen = RT_MIN(uLen - uOffset, pElem->aSegsIn[i].cb);
     189    for (i = uOffset = 0; i < pElem->nIn && uOffset < uLen - uReserved; i++)
     190    {
     191        uint32_t cbSegLen = RT_MIN(uLen - cbReserved - uOffset, pElem->aSegsIn[i].cb - cbReserved);
    192192        if (pElem->aSegsIn[i].pv)
    193193        {
    194194            Log2(("%s vqueuePut: %s used_idx=%u seg=%u addr=%p pv=%p cb=%u acb=%u\n", INSTANCE(pState),
    195195                  QUEUENAME(pState, pQueue), pQueue->uNextUsedIndex, i, pElem->aSegsIn[i].addr, pElem->aSegsIn[i].pv, pElem->aSegsIn[i].cb, cbSegLen));
    196             PDMDevHlpPhysWrite(pState->CTX_SUFF(pDevIns), pElem->aSegsIn[i].addr,
     196            PDMDevHlpPhysWrite(pState->CTX_SUFF(pDevIns), pElem->aSegsIn[i].addr + cbReserved,
    197197                               pElem->aSegsIn[i].pv, cbSegLen);
     198            cbReserved = 0;
    198199        }
    199200        uOffset += cbSegLen;
    200201    }
    201202
     203    Assert((uReserved + uOffset) == uLen || pElem->nIn == 0);
    202204    Log2(("%s vqueuePut: %s used_idx=%u guest_used_idx=%u id=%u len=%u\n", INSTANCE(pState),
    203205          QUEUENAME(pState, pQueue), pQueue->uNextUsedIndex, vringReadUsedIndex(pState, &pQueue->VRing), pElem->uIndex, uLen));
  • trunk/src/VBox/Devices/VirtIO/Virtio.h

    r28800 r33325  
    305305
    306306bool vqueueGet(PVPCISTATE pState, PVQUEUE pQueue, PVQUEUEELEM pElem);
    307 void vqueuePut(PVPCISTATE pState, PVQUEUE pQueue, PVQUEUEELEM pElem, uint32_t uLen);
     307void vqueuePut(PVPCISTATE pState, PVQUEUE pQueue, PVQUEUEELEM pElem, uint32_t uLen, uint32_t uReserved = 0);
    308308void vqueueNotify(PVPCISTATE pState, PVQUEUE pQueue);
    309309void vqueueSync(PVPCISTATE pState, PVQUEUE pQueue);
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