Linux-only fixes: Fix case where a smaller write-combining region
    blocks write-combining setting of the whole frame buffer. Fix bug in wc
    setting code when regions are first splitted and setting of
    write-combining then fails.
This commit is contained in:
Thomas Hellstrom 2005-03-23 21:03:41 +00:00
parent 1be6e2388b
commit f4e9f522fe

View File

@ -178,6 +178,7 @@ struct mtrr_wc_region {
struct mtrr_wc_region * next; struct mtrr_wc_region * next;
}; };
static struct mtrr_wc_region * static struct mtrr_wc_region *
mtrr_cull_wc_region(int screenNum, unsigned long base, unsigned long size, mtrr_cull_wc_region(int screenNum, unsigned long base, unsigned long size,
MessageType from) MessageType from)
@ -187,8 +188,8 @@ mtrr_cull_wc_region(int screenNum, unsigned long base, unsigned long size,
it. */ it. */
struct mtrr_gentry gent; struct mtrr_gentry gent;
char buf[20];
struct mtrr_wc_region *wcreturn = NULL, *wcr; struct mtrr_wc_region *wcreturn = NULL, *wcr;
int count, ret=0;
/* Linux 2.0 users should not get a warning without -verbose */ /* Linux 2.0 users should not get a warning without -verbose */
if (!mtrr_open(2)) if (!mtrr_open(2))
@ -212,23 +213,52 @@ mtrr_cull_wc_region(int screenNum, unsigned long base, unsigned long size,
wcr->sentry.type = MTRR_TYPE_WRCOMB; wcr->sentry.type = MTRR_TYPE_WRCOMB;
wcr->added = FALSE; wcr->added = FALSE;
/* There is now a nicer ioctl-based way to do this, count = 3;
but it isn't in current kernels. */ while (count-- &&
snprintf(buf, sizeof(buf), "disable=%u\n", gent.regnum); (ret = ioctl(mtrr_fd, MTRRIOC_KILL_ENTRY, &(wcr->sentry))) < 0);
if (write(mtrr_fd, buf, strlen(buf)) >= 0) { if (ret >= 0) {
xf86DrvMsg(screenNum, from, xf86DrvMsg(screenNum, from,
"Removed MMIO write-combining range " "Removed MMIO write-combining range "
"(0x%lx,0x%lx)\n", "(0x%lx,0x%lx)\n",
gent.base, gent.size); (unsigned long) gent.base, (unsigned long) gent.size);
wcr->next = wcreturn; wcr->next = wcreturn;
wcreturn = wcr; wcreturn = wcr;
gent.regnum--;
} else { } else {
xfree(wcr); xfree(wcr);
xf86DrvMsgVerb(screenNum, X_WARNING, 0, xf86DrvMsgVerb(screenNum, X_WARNING, 0,
"Failed to remove MMIO " "Failed to remove MMIO "
"write-combining range (0x%lx,0x%lx)\n", "write-combining range (0x%lx,0x%lx)\n",
gent.base, gent.size); gent.base, (unsigned long) gent.size);
}
}
return wcreturn;
}
static struct mtrr_wc_region *
mtrr_remove_offending(int screenNum, unsigned long base, unsigned long size,
MessageType from)
{
struct mtrr_gentry gent;
struct mtrr_wc_region *wcreturn = NULL, **wcr;
if (!mtrr_open(2))
return NULL;
wcr = &wcreturn;
for (gent.regnum = 0;
ioctl(mtrr_fd, MTRRIOC_GET_ENTRY, &gent) >= 0;) {
if (gent.type == MTRR_TYPE_WRCOMB
&& ((gent.base >= base && gent.base + gent.size < base + size) ||
(gent.base > base && gent.base + gent.size <= base + size))) {
*wcr = mtrr_cull_wc_region(screenNum, gent.base, gent.size, from);
while(*wcr) {
wcr = &((*wcr)->next);
}
} else {
gent.regnum++;
} }
} }
return wcreturn; return wcreturn;
@ -239,22 +269,33 @@ static struct mtrr_wc_region *
mtrr_add_wc_region(int screenNum, unsigned long base, unsigned long size, mtrr_add_wc_region(int screenNum, unsigned long base, unsigned long size,
MessageType from) MessageType from)
{ {
struct mtrr_wc_region *wcr; struct mtrr_wc_region **wcr, *wcreturn, *curwcr;
/*
* There can be only one....
*/
wcreturn = mtrr_remove_offending(screenNum, base, size, from);
wcr = &wcreturn;
while (*wcr) {
wcr = &((*wcr)->next);
}
/* Linux 2.0 should not warn, unless the user explicitly asks for /* Linux 2.0 should not warn, unless the user explicitly asks for
WC. */ WC. */
if (!mtrr_open(from == X_CONFIG ? 0 : 2)) if (!mtrr_open(from == X_CONFIG ? 0 : 2))
return NULL; return wcreturn;
wcr = xalloc(sizeof(*wcr)); *wcr = curwcr = xalloc(sizeof(**wcr));
if (!wcr) if (!curwcr)
return NULL; return wcreturn;
wcr->sentry.base = base; curwcr->sentry.base = base;
wcr->sentry.size = size; curwcr->sentry.size = size;
wcr->sentry.type = MTRR_TYPE_WRCOMB; curwcr->sentry.type = MTRR_TYPE_WRCOMB;
wcr->added = TRUE; curwcr->added = TRUE;
wcr->next = NULL; curwcr->next = NULL;
#if SPLIT_WC_REGIONS #if SPLIT_WC_REGIONS
/* /*
@ -279,25 +320,26 @@ mtrr_add_wc_region(int screenNum, unsigned long base, unsigned long size,
if (n_size) { if (n_size) {
xf86DrvMsgVerb(screenNum,X_INFO,3,"Splitting WC range: " xf86DrvMsgVerb(screenNum,X_INFO,3,"Splitting WC range: "
"base: 0x%lx, size: 0x%lx\n",base,size); "base: 0x%lx, size: 0x%lx\n",base,size);
wcr->next = mtrr_add_wc_region(screenNum, n_base, n_size,from); curwcr->next = mtrr_add_wc_region(screenNum, n_base, n_size,from);
} }
wcr->sentry.size = d_size; curwcr->sentry.size = d_size;
} }
/*****************************************************************/ /*****************************************************************/
#endif /* SPLIT_WC_REGIONS */ #endif /* SPLIT_WC_REGIONS */
if (ioctl(mtrr_fd, MTRRIOC_ADD_ENTRY, &wcr->sentry) >= 0) { if (ioctl(mtrr_fd, MTRRIOC_ADD_ENTRY, &curwcr->sentry) >= 0) {
/* Avoid printing on every VT switch */ /* Avoid printing on every VT switch */
if (xf86ServerIsInitialising()) { if (xf86ServerIsInitialising()) {
xf86DrvMsg(screenNum, from, xf86DrvMsg(screenNum, from,
"Write-combining range (0x%lx,0x%lx)\n", "Write-combining range (0x%lx,0x%lx)\n",
base, size); base, size);
} }
return wcr; return wcreturn;
} }
else { else {
xfree(wcr); *wcr = curwcr->next;
xfree(curwcr);
/* Don't complain about the VGA region: MTRR fixed /* Don't complain about the VGA region: MTRR fixed
regions aren't currently supported, but might be in regions aren't currently supported, but might be in
@ -307,7 +349,7 @@ mtrr_add_wc_region(int screenNum, unsigned long base, unsigned long size,
"Failed to set up write-combining range " "Failed to set up write-combining range "
"(0x%lx,0x%lx)\n", base, size); "(0x%lx,0x%lx)\n", base, size);
} }
return NULL; return wcreturn;
} }
} }