Allow physical-memory allocations within stolen memory.

Because stolen memory happens to be a contiguous block of high system memory,
we can just read the GTT entries for it to get physical addresses for our
allocations there if needed.  This reduces fragmentation of the aperture space,
and will often reclaim up to 7 MB of memory that had been left unused since the
simplified aperture manager was put in place, but without reintroducing the
complexities of the old aperture manager.
This commit is contained in:
Eric Anholt 2007-04-30 17:13:09 -07:00
parent 7d0d34cfdc
commit a4f1a7872f
5 changed files with 158 additions and 44 deletions

View File

@ -233,6 +233,7 @@ union intfloat {
#define INREG8(addr) *(volatile CARD8 *)(RecPtr->MMIOBase + (addr))
#define INREG16(addr) *(volatile CARD16 *)(RecPtr->MMIOBase + (addr))
#define INREG(addr) *(volatile CARD32 *)(RecPtr->MMIOBase + (addr))
#define INGTT(addr) *(volatile CARD32 *)(RecPtr->GTTBase + (addr))
#define POSTING_READ(addr) (void)INREG(addr)
#define OUTREG8(addr, val) do { \

View File

@ -526,6 +526,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define PGETBL_SIZE_256KB (1 << 1)
#define PGETBL_SIZE_128KB (2 << 1)
#define I830_PTE_BASE 0x10000
#define PTE_ADDRESS_MASK 0xfffff000
#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */
#define PTE_MAPPING_TYPE_UNCACHED (0 << 1)
#define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */
#define PTE_MAPPING_TYPE_CACHED (3 << 1)
#define PTE_MAPPING_TYPE_MASK (3 << 1)
#define PTE_VALID (1 << 0)
/** @defgroup PGE_ERR
* @{
*/
@ -577,18 +586,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# define PGTBL_ERR_HOST_GTT_PTE (1 << 0)
/** @} */
/* Page table entries loaded via mmio region, p323
*/
#define PTE_BASE 0x10000
#define PTE_ADDR_MASK 0x3FFFF000
#define PTE_TYPE_MASK 0x00000006
#define PTE_LOCAL 0x00000002
#define PTE_MAIN_UNCACHED 0x00000000
#define PTE_MAIN_CACHED 0x00000006
#define PTE_VALID_MASK 0x00000001
#define PTE_VALID 0x00000001
/* Ring buffer registers, p277, overview p19
*/
#define LP_RING 0x2030

View File

@ -261,6 +261,7 @@ enum last_3d {
typedef struct _I830Rec {
unsigned char *MMIOBase;
unsigned char *GTTBase;
unsigned char *FbBase;
int cpp;

View File

@ -521,6 +521,32 @@ I830MapMMIO(ScrnInfoPtr pScrn)
pI830->MMIOAddr, I810_REG_SIZE);
if (!pI830->MMIOBase)
return FALSE;
/* Set up the GTT mapping for the various places it has been moved over
* time.
*/
if (IS_I9XX(pI830)) {
if (IS_I965G(pI830)) {
pI830->GTTBase = xf86MapPciMem(pScrn->scrnIndex, mmioFlags,
pI830->PciTag,
pI830->MMIOAddr + (512 * 1024),
512 * 1024);
if (pI830->GTTBase == NULL)
return FALSE;
} else {
CARD32 gttaddr = pI830->PciInfo->memBase[3] & 0xFFFFFF00;
pI830->GTTBase = xf86MapPciMem(pScrn->scrnIndex, mmioFlags,
pI830->PciTag,
gttaddr,
pI830->FbMapSize / 1024);
if (pI830->GTTBase == NULL)
return FALSE;
}
} else {
pI830->GTTBase = pI830->MMIOBase + I830_PTE_BASE;
}
return TRUE;
}
@ -1136,6 +1162,27 @@ I830PreInit(ScrnInfoPtr pScrn, int flags)
}
xf86CrtcSetSizeRange (pScrn, 320, 200, max_width, max_height);
if (IS_I830(pI830) || IS_845G(pI830)) {
PCITAG bridge;
CARD16 gmch_ctrl;
bridge = pciTag(0, 0, 0); /* This is always the host bridge */
gmch_ctrl = pciReadWord(bridge, I830_GMCH_CTRL);
if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) {
pI830->FbMapSize = 0x8000000;
} else {
pI830->FbMapSize = 0x4000000; /* 64MB - has this been tested ?? */
}
} else {
if (IS_I9XX(pI830)) {
pI830->FbMapSize = 1UL << pciGetBaseSize(pI830->PciTag, 2, TRUE,
NULL);
} else {
/* 128MB aperture for later i8xx series. */
pI830->FbMapSize = 0x8000000;
}
}
/* Some of the probing needs MMIO access, so map it here. */
I830MapMMIO(pScrn);
@ -1159,27 +1206,6 @@ I830PreInit(ScrnInfoPtr pScrn, int flags)
(1 << 23) | (2 << 16));
#endif
if (IS_I830(pI830) || IS_845G(pI830)) {
PCITAG bridge;
CARD16 gmch_ctrl;
bridge = pciTag(0, 0, 0); /* This is always the host bridge */
gmch_ctrl = pciReadWord(bridge, I830_GMCH_CTRL);
if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) {
pI830->FbMapSize = 0x8000000;
} else {
pI830->FbMapSize = 0x4000000; /* 64MB - has this been tested ?? */
}
} else {
if (IS_I9XX(pI830)) {
pI830->FbMapSize = 1UL << pciGetBaseSize(pI830->PciTag, 2, TRUE,
NULL);
} else {
/* 128MB aperture for later i8xx series. */
pI830->FbMapSize = 0x8000000;
}
}
if (pI830->PciInfo->chipType == PCI_CHIP_E7221_G)
num_pipe = 1;
else

View File

@ -309,6 +309,86 @@ i830_allocator_init(ScrnInfoPtr pScrn, unsigned long offset,
return TRUE;
}
/**
* Reads a GTT entry for the memory at the given offset and returns the
* physical address.
*
* \return physical address if successful.
* \return (unsigned long)-1 if unsuccessful.
*/
static unsigned long
i830_get_gtt_physical(ScrnInfoPtr pScrn, unsigned long offset)
{
I830Ptr pI830 = I830PTR(pScrn);
CARD32 gttentry = INGTT(offset / 1024);
/* Mask out these reserved bits on this hardware. */
if (!IS_I965G(pI830))
gttentry &= ~PTE_ADDRESS_MASK_HIGH;
/* If it's not a mapping type we know, then bail. */
if ((gttentry & PTE_MAPPING_TYPE_MASK) != PTE_MAPPING_TYPE_UNCACHED &&
(gttentry & PTE_MAPPING_TYPE_MASK) != PTE_MAPPING_TYPE_CACHED)
{
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Unusable physical mapping type 0x%08x\n",
(unsigned int)(gttentry & PTE_MAPPING_TYPE_MASK));
return -1;
}
/* If we can't represent the address with an unsigned long, bail. */
if (sizeof(unsigned long) == 4 &&
(gttentry & PTE_ADDRESS_MASK_HIGH) != 0)
{
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"High memory PTE (0x%08x) on 32-bit system\n",
(unsigned int)gttentry);
return -1;
}
assert((gttentry & PTE_VALID) != 0);
return (gttentry & PTE_ADDRESS_MASK) |
(gttentry & PTE_ADDRESS_MASK_HIGH << (32 - 4));
}
/**
* Reads the GTT entries for stolen memory at the given offset, returning the
* physical address.
*
* \return physical address if successful.
* \return (unsigned long)-1 if unsuccessful.
*/
static unsigned long
i830_get_stolen_physical(ScrnInfoPtr pScrn, unsigned long offset,
unsigned long size)
{
I830Ptr pI830 = I830PTR(pScrn);
unsigned long physical, scan;
/* Check that the requested region is within stolen memory. */
if (offset + size >= pI830->stolen_size)
return -1;
physical = i830_get_gtt_physical(pScrn, offset);
if (physical == -1)
return -1;
/* Check that the following pages in our allocation follow the first page
* contiguously.
*/
for (scan = offset + 4096; scan < offset + size; scan += 4096) {
unsigned long scan_physical = i830_get_gtt_physical(pScrn, scan);
if ((scan - offset) != (scan_physical - physical)) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Non-contiguous GTT entries: (%ld,%ld) vs (%ld,%ld)\n",
scan, scan_physical, offset, physical);
return -1;
}
}
return physical;
}
/* Allocate aperture space for the given size and alignment, and returns the
* memory allocation.
*
@ -341,13 +421,25 @@ i830_allocate_aperture(ScrnInfoPtr pScrn, const char *name,
alignment = GTT_PAGE_SIZE;
for (scan = pI830->memory_list; scan->next != NULL; scan = scan->next) {
mem->offset = scan->end;
/* For allocations requiring physical addresses, we have to use AGP
* memory, so move the allocation up out of stolen memory.
*/
if ((flags & NEED_PHYSICAL_ADDR) && mem->offset < pI830->stolen_size)
mem->offset = pI830->stolen_size;
mem->offset = ROUND_TO(mem->offset, alignment);
mem->offset = ROUND_TO(scan->end, alignment);
if ((flags & NEED_PHYSICAL_ADDR) && mem->offset < pI830->stolen_size) {
/* If the allocation is entirely within stolen memory, and we're
* able to get the physical addresses out of the GTT and check that
* it's contiguous (it ought to be), then we can do our physical
* allocations there and not bother the kernel about it. This
* helps avoid aperture fragmentation from our physical
* allocations.
*/
mem->bus_addr = i830_get_stolen_physical(pScrn, mem->offset,
mem->size);
if (mem->bus_addr == ((unsigned long)-1)) {
/* Move the start of the allocation to just past the end of
* stolen memory.
*/
mem->offset = ROUND_TO(pI830->stolen_size, alignment);
}
}
mem->end = mem->offset + size;
if (flags & ALIGN_BOTH_ENDS)
@ -385,11 +477,8 @@ i830_allocate_agp_memory(ScrnInfoPtr pScrn, i830_memory *mem, int flags)
if (mem->key != -1)
return TRUE;
if (mem->offset + mem->size <= pI830->stolen_size &&
!(flags & NEED_PHYSICAL_ADDR))
{
if (mem->offset + mem->size <= pI830->stolen_size)
return TRUE;
}
if (mem->offset < pI830->stolen_size)
mem->agp_offset = pI830->stolen_size;