[PATCH 4/6] Use MTRR for pci_mmap_resource_wc if PAT is not available

From: Thomas Schlichter
Date: Sat Oct 17 2009 - 15:17:16 EST


X.org uses libpciaccess which tries to mmap with write combining enabled via
/sys/bus/pci/devices/*/resource0_wc. Currently, when PAT is not enabled, we
fall back to uncached mmap. Then libpciaccess thinks it succeeded mapping
with write combining anabled and does not set up suited MTRR entries. ;-(

So when falling back to uncached mapping, we better try to set up MTRR
entries automatically. When the resource file is closed, we remove the MTRR
entries again.

Signed-off-by: Thomas Schlichter <thomas.schlichter@xxxxxx>
---
drivers/pci/pci-sysfs.c | 45 +++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 0f6382f..604a063 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -23,6 +23,11 @@
#include <linux/mm.h>
#include <linux/capability.h>
#include <linux/pci-aspm.h>
+#include <linux/fs.h>
+#ifdef CONFIG_X86
+# include <asm/mtrr.h>
+# include <asm/pat.h>
+#endif
#include "pci.h"

static int sysfs_initialized; /* = 0 */
@@ -692,9 +697,10 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
struct pci_dev *pdev = to_pci_dev(container_of(kobj,
struct device, kobj));
struct resource *res = (struct resource *)attr->private;
+ struct bin_buffer *bb = (struct bin_buffer *)vma->vm_file->private_data;
enum pci_mmap_state mmap_type;
resource_size_t start, end;
- int i;
+ int rc, i;

for (i = 0; i < PCI_ROM_RESOURCE; i++)
if (res == &pdev->resource[i])
@@ -716,7 +722,18 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(start))
return -EINVAL;

- return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
+ rc = pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
+#ifdef CONFIG_X86
+ if (!rc && !pat_enabled && write_combine) {
+ if (!bb->private)
+ bb->private = kzalloc(num_var_ranges * sizeof(int),
+ GFP_KERNEL);
+ mtrr_add_unaligned(vma->vm_pgoff << PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ MTRR_TYPE_WRCOMB, true, bb->private);
+ }
+#endif // CONFIG_X86
+ return rc;
}

static int
@@ -733,6 +750,29 @@ pci_mmap_resource_wc(struct kobject *kobj, struct bin_attribute *attr,
return pci_mmap_resource(kobj, attr, vma, 1);
}

+static int
+pci_release(struct kobject *kobj, struct bin_attribute *attr, struct file *file)
+{
+#ifdef CONFIG_X86
+ struct bin_buffer *bb = (struct bin_buffer *)file->private_data;
+ int i, *mtrr_usage = (int *)bb->private;
+
+ if (!mtrr_usage)
+ return 0;
+
+ for (i = 0; i < num_var_ranges; ++i) {
+ while (mtrr_usage[i] > 0) {
+ mtrr_del(i, 0, 0);
+ --mtrr_usage[i];
+ }
+ }
+
+ kfree(bb->private);
+ bb->private = NULL;
+#endif // CONFIG_X86
+ return 0;
+}
+
/**
* pci_remove_resource_files - cleanup resource files
* @pdev: dev to cleanup
@@ -782,6 +822,7 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
sprintf(res_attr_name, "resource%d", num);
res_attr->mmap = pci_mmap_resource_uc;
}
+ res_attr->release = pci_release;
res_attr->attr.name = res_attr_name;
res_attr->attr.mode = S_IRUSR | S_IWUSR;
res_attr->size = pci_resource_len(pdev, num);
--
1.6.5


--Boundary-00=_U+h2K2gddSw6RDO
Content-Type: text/x-patch;
charset="iso-8859-1";
name="0006-Set-up-MTRR-entries-within-set_memory_wc.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="0006-Set-up-MTRR-entries-within-set_memory_wc.patch"