VirtualBox

Changeset 26563 in vbox


Ignore:
Timestamp:
Feb 16, 2010 9:50:44 AM (15 years ago)
Author:
vboxsync
Message:

Started with large page support

Location:
trunk
Files:
4 edited

Legend:

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

    r26414 r26563  
    271271GMMR0DECL(int)  GMMR0AllocateHandyPages(PVM pVM, VMCPUID idCpu, uint32_t cPagesToUpdate, uint32_t cPagesToAlloc, PGMMPAGEDESC paPages);
    272272GMMR0DECL(int)  GMMR0AllocatePages(PVM pVM, VMCPUID idCpu, uint32_t cPages, PGMMPAGEDESC paPages, GMMACCOUNT enmAccount);
     273GMMR0DECL(int)  GMMR0AllocateLargePage(PVM pVM, VMCPUID idCpu, uint64_t cbPage, uint32_t *pidPage);
    273274GMMR0DECL(int)  GMMR0FreePages(PVM pVM, VMCPUID idCpu, uint32_t cPages, PGMMFREEPAGEDESC paPages, GMMACCOUNT enmAccount);
     275GMMR0DECL(int)  GMMR0FreeLargePage(PVM pVM, VMCPUID idCpu, uint32_t idPage);
    274276GMMR0DECL(int)  GMMR0BalloonedPages(PVM pVM, VMCPUID idCpu, uint32_t cBalloonedPages, uint32_t cPagesToFree, PGMMFREEPAGEDESC paPages, bool fCompleted);
    275277GMMR0DECL(int)  GMMR0DeflatedBalloon(PVM pVM, VMCPUID idCpu, uint32_t cPages);
     
    401403
    402404GMMR0DECL(int)  GMMR0MapUnmapChunkReq(PVM pVM, VMCPUID idCpu, PGMMMAPUNMAPCHUNKREQ pReq);
     405
     406/**
     407 * Request buffer for GMMR0AllocateLargePageReq / VMMR0_DO_GMM_ALLOC_LARGE_PAGE.
     408 * @see GMMR0AllocateLargePage
     409 */
     410typedef struct GMMALLOCLARGEPAGEREQ
     411{
     412    /** The header. */
     413    SUPVMMR0REQHDR  Hdr;
     414    /* Large page size. */
     415    uint32_t        cbPage;
     416    /** The Page ID.
     417     *
     418     * @output  The ID of the page, NIL_GMM_PAGEID if the allocation failed.
     419     */
     420    uint32_t        idPage;
     421} GMMALLOCLARGEPAGEREQ;
     422/** Pointer to a GMMR0AllocateLargePageReq / VMMR0_DO_GMM_ALLOC_LARGE_PAGE request buffer. */
     423typedef GMMALLOCLARGEPAGEREQ *PGMMALLOCLARGEPAGEREQ;
     424
     425GMMR0DECL(int) GMMR0AllocateLargePageReq(PVM pVM, VMCPUID idCpu, PGMMALLOCLARGEPAGEREQ pReq);
     426
     427
     428/**
     429 * Request buffer for GMMR0FreeLargePageReq / VMMR0_DO_GMM_FREE_LARGE_PAGE.
     430 * @see GMMR0FreeLargePage.
     431 */
     432typedef struct GMMFREELARGEPAGEREQ
     433{
     434    /** The header. */
     435    SUPVMMR0REQHDR  Hdr;
     436    /** The Page ID. */
     437    uint32_t        idPage;
     438} GMMFREELARGEPAGEREQ;
     439/** Pointer to a GMMR0FreePagesReq / VMMR0_DO_GMM_FREE_PAGES request buffer. */
     440typedef GMMFREELARGEPAGEREQ *PGMMFREELARGEPAGEREQ;
     441
     442GMMR0DECL(int) GMMR0FreeLargePageReq(PVM pVM, VMCPUID idCpu, PGMMFREELARGEPAGEREQ pReq);
    403443
    404444
  • trunk/include/VBox/vmm.h

    r23145 r26563  
    292292    /** Call GMMR0AllocatePages(). */
    293293    VMMR0_DO_GMM_ALLOCATE_PAGES,
     294    /** Call GMMR0AllocateLargePage(). */
     295    VMMR0_DO_GMM_ALLOC_LARGE_PAGE,
    294296    /** Call GMMR0FreePages(). */
    295297    VMMR0_DO_GMM_FREE_PAGES,
     298    /** Call GMMR0FreeLargePage(). */
     299    VMMR0_DO_GMM_FREE_LARGE_PAGE,
    296300    /** Call GMMR0BalloonedPages(). */
    297301    VMMR0_DO_GMM_BALLOONED_PAGES,
  • trunk/src/VBox/VMM/VMMR0/GMMR0.cpp

    r26542 r26563  
    15291529 * Registers a new chunk of memory.
    15301530 *
    1531  * This is called by both gmmR0AllocateOneChunk and GMMR0SeedChunk. Will take
    1532  * the mutex, the caller must not own it.
     1531 * This is called by both gmmR0AllocateOneChunk and GMMR0SeedChunk. The caller
     1532 * must own the global lock.
    15331533 *
    15341534 * @returns VBox status code.
     
    15391539 *                          affinity.
    15401540 * @param   enmChunkType    Chunk type (continuous or non-continuous)
    1541  */
    1542 static int gmmR0RegisterChunk(PGMM pGMM, PGMMCHUNKFREESET pSet, RTR0MEMOBJ MemObj, uint16_t hGVM, GMMCHUNKTYPE enmChunkType)
     1541 * @param   ppChunk         Chunk address (out)
     1542 */
     1543static int gmmR0RegisterChunk(PGMM pGMM, PGMMCHUNKFREESET pSet, RTR0MEMOBJ MemObj, uint16_t hGVM, GMMCHUNKTYPE enmChunkType, PGMMCHUNK *ppChunk = NULL)
    15431544{
    15441545    Assert(hGVM != NIL_GVM_HANDLE || pGMM->fBoundMemoryMode);
     
    15681569         * This has to be done behind the mutex of course.
    15691570         */
    1570         rc = RTSemFastMutexRequest(pGMM->Mtx);
    1571         if (RT_SUCCESS(rc))
    1572         {
    1573             if (GMM_CHECK_SANITY_UPON_ENTERING(pGMM))
     1571        if (GMM_CHECK_SANITY_UPON_ENTERING(pGMM))
     1572        {
     1573            pChunk->Core.Key = gmmR0AllocateChunkId(pGMM);
     1574            if (    pChunk->Core.Key != NIL_GMM_CHUNKID
     1575                &&  pChunk->Core.Key <= GMM_CHUNKID_LAST
     1576                &&  RTAvlU32Insert(&pGMM->pChunks, &pChunk->Core))
    15741577            {
    1575                 pChunk->Core.Key = gmmR0AllocateChunkId(pGMM);
    1576                 if (    pChunk->Core.Key != NIL_GMM_CHUNKID
    1577                     &&  pChunk->Core.Key <= GMM_CHUNKID_LAST
    1578                     &&  RTAvlU32Insert(&pGMM->pChunks, &pChunk->Core))
    1579                 {
    1580                     pGMM->cChunks++;
    1581                     gmmR0LinkChunk(pChunk, pSet);
    1582                     LogFlow(("gmmR0RegisterChunk: pChunk=%p id=%#x cChunks=%d\n", pChunk, pChunk->Core.Key, pGMM->cChunks));
    1583 
    1584                     GMM_CHECK_SANITY_UPON_LEAVING(pGMM);
    1585                     RTSemFastMutexRelease(pGMM->Mtx);
    1586                     return VINF_SUCCESS;
    1587                 }
    1588 
    1589                 /* bail out */
    1590                 rc = VERR_INTERNAL_ERROR;
     1578                pGMM->cChunks++;
     1579                gmmR0LinkChunk(pChunk, pSet);
     1580                LogFlow(("gmmR0RegisterChunk: pChunk=%p id=%#x cChunks=%d\n", pChunk, pChunk->Core.Key, pGMM->cChunks));
     1581
     1582                if (ppChunk)
     1583                    *ppChunk = pChunk;
     1584
     1585                GMM_CHECK_SANITY_UPON_LEAVING(pGMM);
     1586                RTSemFastMutexRelease(pGMM->Mtx);
     1587                return VINF_SUCCESS;
    15911588            }
    1592             else
    1593                 rc = VERR_INTERNAL_ERROR_5;
    1594 
    1595             RTSemFastMutexRelease(pGMM->Mtx);
    1596         }
     1589
     1590            /* bail out */
     1591            rc = VERR_INTERNAL_ERROR;
     1592        }
     1593        else
     1594            rc = VERR_INTERNAL_ERROR_5;
     1595
    15971596        RTMemFree(pChunk);
    15981597    }
     
    16111610 * @param   hGVM            The affinity of the new chunk.
    16121611 * @param   enmChunkType    Chunk type (continuous or non-continuous)
     1612 * @param   ppChunk         Chunk address (out)
    16131613 *
    16141614 * @remarks Called without owning the mutex.
    16151615 */
    1616 static int gmmR0AllocateOneChunk(PGMM pGMM, PGMMCHUNKFREESET pSet, uint16_t hGVM, GMMCHUNKTYPE enmChunkType)
     1616static int gmmR0AllocateOneChunk(PGMM pGMM, PGMMCHUNKFREESET pSet, uint16_t hGVM, GMMCHUNKTYPE enmChunkType, PGMMCHUNK *ppChunk = NULL)
    16171617{
    16181618    /*
     
    16251625    AssertReturn(enmChunkType == GMMCHUNKTYPE_NON_CONTINUOUS || enmChunkType == GMMCHUNKTYPE_CONTINUOUS, VERR_INVALID_PARAMETER);
    16261626
     1627    /* Leave the lock temporarily as the allocation might take long. */
     1628    RTSemFastMutexRelease(pGMM->Mtx);
    16271629    if (enmChunkType == GMMCHUNKTYPE_NON_CONTINUOUS)
    16281630        rc = RTR0MemObjAllocPhysNC(&MemObj, GMM_CHUNK_SIZE, NIL_RTHCPHYS);
     
    16301632        rc = RTR0MemObjAllocPhysEx(&MemObj, GMM_CHUNK_SIZE, NIL_RTHCPHYS, GMM_CHUNK_SIZE);
    16311633
     1634    /* Grab the lock again. */
     1635    int rc2 = RTSemFastMutexRequest(pGMM->Mtx);
     1636    AssertRCReturn(rc2, rc2);
     1637
    16321638    if (RT_SUCCESS(rc))
    16331639    {
    1634         rc = gmmR0RegisterChunk(pGMM, pSet, MemObj, hGVM, enmChunkType);
     1640        rc = gmmR0RegisterChunk(pGMM, pSet, MemObj, hGVM, enmChunkType, ppChunk);
    16351641        if (RT_FAILURE(rc))
    16361642            RTR0MemObjFree(MemObj, false /* fFreeMappings */);
     
    16831689         * If we need still more pages, allocate new chunks.
    16841690         * Note! We will leave the mutex while doing the allocation,
    1685          *       gmmR0AllocateOneChunk will re-take it temporarily while registering the chunk.
    16861691         */
    16871692        while (pSet->cFreePages < cPages)
    16881693        {
    1689             RTSemFastMutexRelease(pGMM->Mtx);
    16901694            int rc = gmmR0AllocateOneChunk(pGMM, pSet, pGVM->hSelf, GMMCHUNKTYPE_NON_CONTINUOUS);
    1691             int rc2 = RTSemFastMutexRequest(pGMM->Mtx);
    1692             AssertRCReturn(rc2, rc2);
    16931695            if (RT_FAILURE(rc))
    16941696                return rc;
     
    17051707         *
    17061708         * Note! We will leave the mutex while doing the allocation,
    1707          *       gmmR0AllocateOneChunk will re-take it temporarily while registering the chunk.
    17081709         */
    17091710        uint16_t const hGVM = pGVM->hSelf;
     
    17241725
    17251726            /* Allocate more. */
    1726             RTSemFastMutexRelease(pGMM->Mtx);
    17271727            int rc = gmmR0AllocateOneChunk(pGMM, pSet, hGVM, GMMCHUNKTYPE_NON_CONTINUOUS);
    1728             int rc2 = RTSemFastMutexRequest(pGMM->Mtx);
    1729             AssertRCReturn(rc2, rc2);
    17301728            if (RT_FAILURE(rc))
    17311729                return rc;
     
    22772275}
    22782276
     2277/**
     2278 * Allocate a large page to represent guest RAM
     2279 *
     2280 * The allocated pages are not cleared and will contains random garbage.
     2281 *
     2282 * @returns VBox status code:
     2283 * @retval  VINF_SUCCESS on success.
     2284 * @retval  VERR_NOT_OWNER if the caller is not an EMT.
     2285 * @retval  VERR_GMM_SEED_ME if seeding via GMMR0SeedChunk is necessary.
     2286 * @retval  VERR_GMM_HIT_GLOBAL_LIMIT if we've exhausted the available pages.
     2287 * @retval  VERR_GMM_HIT_VM_ACCOUNT_LIMIT if we've hit the VM account limit,
     2288 *          that is we're trying to allocate more than we've reserved.
     2289 * @returns see GMMR0AllocatePages.
     2290 * @param   pVM             Pointer to the shared VM structure.
     2291 * @param   idCpu           VCPU id
     2292 * @param   cbPage          Large page size
     2293 * @param   pidPage         Id of the page (out)
     2294 */
     2295GMMR0DECL(int)  GMMR0AllocateLargePage(PVM pVM, VMCPUID idCpu, uint32_t cbPage, uint32_t *pidPage)
     2296{
     2297    LogFlow(("GMMR0AllocateLargePage: pVM=%p cbPage=%x\n", pVM, cbPage));
     2298
     2299    AssertReturn(cbPage == GMM_CHUNK_SIZE, VERR_INVALID_PARAMETER);
     2300
     2301    /*
     2302     * Validate, get basics and take the semaphore.
     2303     */
     2304    PGMM pGMM;
     2305    GMM_GET_VALID_INSTANCE(pGMM, VERR_INTERNAL_ERROR);
     2306    PGVM pGVM;
     2307    int rc = GVMMR0ByVMAndEMT(pVM, idCpu, &pGVM);
     2308    if (RT_FAILURE(rc))
     2309        return rc;
     2310
     2311    /* Not supported in legacy mode where we allocate the memory in ring 3 and lock it in ring 0. */
     2312    if (pGMM->fLegacyAllocationMode)
     2313        return VERR_NOT_SUPPORTED;
     2314
     2315    *pidPage = NIL_GMM_PAGEID;
     2316
     2317    rc = RTSemFastMutexRequest(pGMM->Mtx);
     2318    AssertRCReturn(rc, rc);
     2319    if (GMM_CHECK_SANITY_UPON_ENTERING(pGMM))
     2320    {
     2321        const unsigned cPages = (GMM_CHUNK_SIZE >> PAGE_SHIFT);
     2322        PGMMCHUNK      pChunk;
     2323        GMMPAGEDESC    PageDesc;
     2324
     2325        if (RT_UNLIKELY(pGVM->gmm.s.Allocated.cBasePages + cPages > pGVM->gmm.s.Reserved.cBasePages))
     2326        {
     2327            Log(("GMMR0AllocateLargePage: Reserved=%#llx Allocated+Requested=%#llx+%#x!\n",
     2328                 pGVM->gmm.s.Reserved.cBasePages, pGVM->gmm.s.Allocated.cBasePages, cPages));
     2329            RTSemFastMutexRelease(pGMM->Mtx);
     2330            return VERR_GMM_HIT_VM_ACCOUNT_LIMIT;
     2331        }
     2332
     2333        /* Allocate a new continous chunk. */
     2334        rc = gmmR0AllocateOneChunk(pGMM, &pGMM->Private, pGVM->hSelf, GMMCHUNKTYPE_CONTINUOUS, &pChunk);
     2335        if (RT_FAILURE(rc))
     2336        {
     2337            RTSemFastMutexRelease(pGMM->Mtx);
     2338            return rc;
     2339        }
     2340
     2341        /* Unlink the new chunk from the free list. */
     2342        gmmR0UnlinkChunk(pChunk);
     2343
     2344        /* Allocate all pages. */
     2345        gmmR0AllocatePage(pGMM, pGVM->hSelf, pChunk, &PageDesc);
     2346        /* Return the first page as we'll use the whole chunk as one big page. */
     2347        *pidPage = PageDesc.idPage;
     2348
     2349        for (unsigned i = 1; i < cPages; i++)
     2350            gmmR0AllocatePage(pGMM, pGVM->hSelf, pChunk, &PageDesc);
     2351
     2352        /* Update accounting. */
     2353        pGVM->gmm.s.Allocated.cBasePages += cPages;
     2354        pGVM->gmm.s.cPrivatePages        += cPages;
     2355        pGMM->cAllocatedPages            += cPages;
     2356
     2357        gmmR0LinkChunk(pChunk, &pGMM->Private);
     2358    }
     2359    else
     2360        rc = VERR_INTERNAL_ERROR_5;
     2361
     2362    RTSemFastMutexRelease(pGMM->Mtx);
     2363    LogFlow(("GMMR0AllocatePages: returns %Rrc\n", rc));
     2364    return rc;
     2365}
     2366
     2367
     2368/**
     2369 * VMMR0 request wrapper for GMMR0AllocateLargePage.
     2370 *
     2371 * @returns see GMMR0AllocateLargePage.
     2372 * @param   pVM             Pointer to the shared VM structure.
     2373 * @param   idCpu           VCPU id
     2374 * @param   pReq            The request packet.
     2375 */
     2376GMMR0DECL(int) GMMR0AllocateLargePageReq(PVM pVM, VMCPUID idCpu, PGMMALLOCLARGEPAGEREQ pReq)
     2377{
     2378    /*
     2379     * Validate input and pass it on.
     2380     */
     2381    AssertPtrReturn(pVM, VERR_INVALID_POINTER);
     2382    AssertPtrReturn(pReq, VERR_INVALID_POINTER);
     2383    AssertMsgReturn(pReq->Hdr.cbReq == sizeof(GMMALLOCATEPAGESREQ),
     2384                    ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(GMMALLOCATEPAGESREQ)),
     2385                    VERR_INVALID_PARAMETER);
     2386
     2387    return GMMR0AllocateLargePage(pVM, idCpu, pReq->cbPage, &pReq->idPage);
     2388}
     2389
     2390
     2391/**
     2392 * Free a large page
     2393 *
     2394 * @returns VBox status code:
     2395 * @param   pVM             Pointer to the shared VM structure.
     2396 * @param   idCpu           VCPU id
     2397 * @param   idPage          Large page id
     2398 */
     2399GMMR0DECL(int)  GMMR0FreeLargePage(PVM pVM, VMCPUID idCpu, uint32_t idPage)
     2400{
     2401    LogFlow(("GMMR0FreeLargePage: pVM=%p idPage=%x\n", pVM, idPage));
     2402
     2403    /*
     2404     * Validate, get basics and take the semaphore.
     2405     */
     2406    PGMM pGMM;
     2407    GMM_GET_VALID_INSTANCE(pGMM, VERR_INTERNAL_ERROR);
     2408    PGVM pGVM;
     2409    int rc = GVMMR0ByVMAndEMT(pVM, idCpu, &pGVM);
     2410    if (RT_FAILURE(rc))
     2411        return rc;
     2412
     2413    /* Not supported in legacy mode where we allocate the memory in ring 3 and lock it in ring 0. */
     2414    if (pGMM->fLegacyAllocationMode)
     2415        return VERR_NOT_SUPPORTED;
     2416
     2417    rc = RTSemFastMutexRequest(pGMM->Mtx);
     2418    AssertRC(rc);
     2419    if (GMM_CHECK_SANITY_UPON_ENTERING(pGMM))
     2420    {
     2421        const unsigned cPages = (GMM_CHUNK_SIZE >> PAGE_SHIFT);
     2422
     2423        if (RT_UNLIKELY(pGVM->gmm.s.Allocated.cBasePages < cPages))
     2424        {
     2425            Log(("gmmR0FreePages: allocated=%#llx cPages=%#x!\n", pGVM->gmm.s.Allocated.cBasePages, cPages));
     2426            RTSemFastMutexRelease(pGMM->Mtx);
     2427            return VERR_GMM_ATTEMPT_TO_FREE_TOO_MUCH;
     2428        }
     2429
     2430        PGMMPAGE pPage = gmmR0GetPage(pGMM, idPage);
     2431        if (    RT_LIKELY(pPage)
     2432            &&  RT_LIKELY(GMM_PAGE_IS_PRIVATE(pPage)))
     2433        {
     2434                PGMMCHUNK pChunk = gmmR0GetChunk(pGMM, idPage >> GMM_CHUNKID_SHIFT);
     2435                Assert(pChunk);
     2436                Assert(pChunk->cFree < GMM_CHUNK_NUM_PAGES);
     2437                Assert(pChunk->cPrivate > 0);
     2438
     2439                /* Release the memory immediately. */
     2440                gmmR0FreeChunk(pGMM, NULL, pChunk);
     2441
     2442                /* Update accounting. */
     2443                pGVM->gmm.s.Allocated.cBasePages -= cPages;
     2444                pGVM->gmm.s.cPrivatePages        -= cPages;
     2445                pGMM->cAllocatedPages            -= cPages;
     2446        }
     2447        else
     2448            rc = VERR_GMM_PAGE_NOT_FOUND;
     2449    }
     2450    else
     2451        rc = VERR_INTERNAL_ERROR_5;
     2452
     2453    RTSemFastMutexRelease(pGMM->Mtx);
     2454    LogFlow(("GMMR0FreeLargePage: returns %Rrc\n", rc));
     2455    return rc;
     2456}
     2457
     2458
     2459/**
     2460 * VMMR0 request wrapper for GMMR0FreeLargePage.
     2461 *
     2462 * @returns see GMMR0FreeLargePage.
     2463 * @param   pVM             Pointer to the shared VM structure.
     2464 * @param   idCpu           VCPU id
     2465 * @param   pReq            The request packet.
     2466 */
     2467GMMR0DECL(int) GMMR0FreeLargePageReq(PVM pVM, VMCPUID idCpu, PGMMFREELARGEPAGEREQ pReq)
     2468{
     2469    /*
     2470     * Validate input and pass it on.
     2471     */
     2472    AssertPtrReturn(pVM, VERR_INVALID_POINTER);
     2473    AssertPtrReturn(pReq, VERR_INVALID_POINTER);
     2474    AssertMsgReturn(pReq->Hdr.cbReq == sizeof(GMMFREEPAGESREQ),
     2475                    ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(GMMFREEPAGESREQ)),
     2476                    VERR_INVALID_PARAMETER);
     2477
     2478    return GMMR0FreeLargePage(pVM, idCpu, pReq->idPage);
     2479}
    22792480
    22802481/**
     
    31333334    if (RT_SUCCESS(rc))
    31343335    {
     3336        /* Grab the lock. */
     3337        rc = RTSemFastMutexRequest(pGMM->Mtx);
     3338        AssertRCReturn(rc, rc);
     3339
    31353340        /*
    31363341         * Add a new chunk with our hGVM.
    31373342         */
    31383343        rc = gmmR0RegisterChunk(pGMM, &pGMM->Private, MemObj, pGVM->hSelf, GMMCHUNKTYPE_NON_CONTINUOUS);
     3344        RTSemFastMutexRelease(pGMM->Mtx);
     3345
    31393346        if (RT_FAILURE(rc))
    31403347            RTR0MemObjFree(MemObj, false /* fFreeMappings */);
  • trunk/src/VBox/VMM/VMMR0/VMMR0.cpp

    r26066 r26563  
    895895            return GMMR0AllocatePagesReq(pVM, idCpu, (PGMMALLOCATEPAGESREQ)pReqHdr);
    896896
     897        case VMMR0_DO_GMM_ALLOC_LARGE_PAGE:
     898            if (u64Arg)
     899                return VERR_INVALID_PARAMETER;
     900            return GMMR0AllocateLargePageReq(pVM, idCpu, (PGMMALLOCLARGEPAGEREQ)pReqHdr);
     901
    897902        case VMMR0_DO_GMM_FREE_PAGES:
    898903            if (u64Arg)
    899904                return VERR_INVALID_PARAMETER;
    900905            return GMMR0FreePagesReq(pVM, idCpu, (PGMMFREEPAGESREQ)pReqHdr);
     906
     907        case VMMR0_DO_GMM_FREE_LARGE_PAGE:
     908            if (u64Arg)
     909                return VERR_INVALID_PARAMETER;
     910            return GMMR0FreeLargePageReq(pVM, idCpu, (PGMMFREELARGEPAGEREQ)pReqHdr);
    901911
    902912        case VMMR0_DO_GMM_BALLOONED_PAGES:
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