diff options
| -rw-r--r-- | Documentation/io-mapping.txt | 82 | ||||
| -rw-r--r-- | arch/x86/Kconfig | 4 | ||||
| -rw-r--r-- | arch/x86/include/asm/fixmap.h | 4 | ||||
| -rw-r--r-- | arch/x86/include/asm/fixmap_32.h | 4 | ||||
| -rw-r--r-- | arch/x86/include/asm/highmem.h | 5 | ||||
| -rw-r--r-- | arch/x86/mm/Makefile | 2 | ||||
| -rw-r--r-- | arch/x86/mm/init_32.c | 3 | ||||
| -rw-r--r-- | arch/x86/mm/iomap_32.c | 59 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 3 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_gem.c | 174 | ||||
| -rw-r--r-- | include/asm-x86/iomap.h | 30 | ||||
| -rw-r--r-- | include/linux/io-mapping.h | 125 |
12 files changed, 390 insertions, 105 deletions
diff --git a/Documentation/io-mapping.txt b/Documentation/io-mapping.txt new file mode 100644 index 000000000000..473e43b2d588 --- /dev/null +++ b/Documentation/io-mapping.txt | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | The io_mapping functions in linux/io-mapping.h provide an abstraction for | ||
| 2 | efficiently mapping small regions of an I/O device to the CPU. The initial | ||
| 3 | usage is to support the large graphics aperture on 32-bit processors where | ||
| 4 | ioremap_wc cannot be used to statically map the entire aperture to the CPU | ||
| 5 | as it would consume too much of the kernel address space. | ||
| 6 | |||
| 7 | A mapping object is created during driver initialization using | ||
| 8 | |||
| 9 | struct io_mapping *io_mapping_create_wc(unsigned long base, | ||
| 10 | unsigned long size) | ||
| 11 | |||
| 12 | 'base' is the bus address of the region to be made | ||
| 13 | mappable, while 'size' indicates how large a mapping region to | ||
| 14 | enable. Both are in bytes. | ||
| 15 | |||
| 16 | This _wc variant provides a mapping which may only be used | ||
| 17 | with the io_mapping_map_atomic_wc or io_mapping_map_wc. | ||
| 18 | |||
| 19 | With this mapping object, individual pages can be mapped either atomically | ||
| 20 | or not, depending on the necessary scheduling environment. Of course, atomic | ||
| 21 | maps are more efficient: | ||
| 22 | |||
| 23 | void *io_mapping_map_atomic_wc(struct io_mapping *mapping, | ||
| 24 | unsigned long offset) | ||
| 25 | |||
| 26 | 'offset' is the offset within the defined mapping region. | ||
| 27 | Accessing addresses beyond the region specified in the | ||
| 28 | creation function yields undefined results. Using an offset | ||
| 29 | which is not page aligned yields an undefined result. The | ||
| 30 | return value points to a single page in CPU address space. | ||
| 31 | |||
| 32 | This _wc variant returns a write-combining map to the | ||
| 33 | page and may only be used with mappings created by | ||
| 34 | io_mapping_create_wc | ||
| 35 | |||
| 36 | Note that the task may not sleep while holding this page | ||
| 37 | mapped. | ||
| 38 | |||
| 39 | void io_mapping_unmap_atomic(void *vaddr) | ||
| 40 | |||
| 41 | 'vaddr' must be the the value returned by the last | ||
| 42 | io_mapping_map_atomic_wc call. This unmaps the specified | ||
| 43 | page and allows the task to sleep once again. | ||
| 44 | |||
| 45 | If you need to sleep while holding the lock, you can use the non-atomic | ||
| 46 | variant, although they may be significantly slower. | ||
| 47 | |||
| 48 | void *io_mapping_map_wc(struct io_mapping *mapping, | ||
| 49 | unsigned long offset) | ||
| 50 | |||
| 51 | This works like io_mapping_map_atomic_wc except it allows | ||
| 52 | the task to sleep while holding the page mapped. | ||
| 53 | |||
| 54 | void io_mapping_unmap(void *vaddr) | ||
| 55 | |||
| 56 | This works like io_mapping_unmap_atomic, except it is used | ||
| 57 | for pages mapped with io_mapping_map_wc. | ||
| 58 | |||
| 59 | At driver close time, the io_mapping object must be freed: | ||
| 60 | |||
| 61 | void io_mapping_free(struct io_mapping *mapping) | ||
| 62 | |||
| 63 | Current Implementation: | ||
| 64 | |||
| 65 | The initial implementation of these functions uses existing mapping | ||
| 66 | mechanisms and so provides only an abstraction layer and no new | ||
| 67 | functionality. | ||
| 68 | |||
| 69 | On 64-bit processors, io_mapping_create_wc calls ioremap_wc for the whole | ||
| 70 | range, creating a permanent kernel-visible mapping to the resource. The | ||
| 71 | map_atomic and map functions add the requested offset to the base of the | ||
| 72 | virtual address returned by ioremap_wc. | ||
| 73 | |||
| 74 | On 32-bit processors with HIGHMEM defined, io_mapping_map_atomic_wc uses | ||
| 75 | kmap_atomic_pfn to map the specified page in an atomic fashion; | ||
| 76 | kmap_atomic_pfn isn't really supposed to be used with device pages, but it | ||
| 77 | provides an efficient mapping for this usage. | ||
| 78 | |||
| 79 | On 32-bit processors without HIGHMEM defined, io_mapping_map_atomic_wc and | ||
| 80 | io_mapping_map_wc both use ioremap_wc, a terribly inefficient function which | ||
| 81 | performs an IPI to inform all processors about the new mapping. This results | ||
| 82 | in a significant performance penalty. | ||
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 6f20718d3156..e60c59b81bdd 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
| @@ -1894,6 +1894,10 @@ config SYSVIPC_COMPAT | |||
| 1894 | endmenu | 1894 | endmenu |
| 1895 | 1895 | ||
| 1896 | 1896 | ||
| 1897 | config HAVE_ATOMIC_IOMAP | ||
| 1898 | def_bool y | ||
| 1899 | depends on X86_32 | ||
| 1900 | |||
| 1897 | source "net/Kconfig" | 1901 | source "net/Kconfig" |
| 1898 | 1902 | ||
| 1899 | source "drivers/Kconfig" | 1903 | source "drivers/Kconfig" |
diff --git a/arch/x86/include/asm/fixmap.h b/arch/x86/include/asm/fixmap.h index 8668a94f850e..23696d44a0af 100644 --- a/arch/x86/include/asm/fixmap.h +++ b/arch/x86/include/asm/fixmap.h | |||
| @@ -9,6 +9,10 @@ | |||
| 9 | 9 | ||
| 10 | extern int fixmaps_set; | 10 | extern int fixmaps_set; |
| 11 | 11 | ||
| 12 | extern pte_t *kmap_pte; | ||
| 13 | extern pgprot_t kmap_prot; | ||
| 14 | extern pte_t *pkmap_page_table; | ||
| 15 | |||
| 12 | void __native_set_fixmap(enum fixed_addresses idx, pte_t pte); | 16 | void __native_set_fixmap(enum fixed_addresses idx, pte_t pte); |
| 13 | void native_set_fixmap(enum fixed_addresses idx, | 17 | void native_set_fixmap(enum fixed_addresses idx, |
| 14 | unsigned long phys, pgprot_t flags); | 18 | unsigned long phys, pgprot_t flags); |
diff --git a/arch/x86/include/asm/fixmap_32.h b/arch/x86/include/asm/fixmap_32.h index 09f29ab5c139..c7115c1d7217 100644 --- a/arch/x86/include/asm/fixmap_32.h +++ b/arch/x86/include/asm/fixmap_32.h | |||
| @@ -28,10 +28,8 @@ extern unsigned long __FIXADDR_TOP; | |||
| 28 | #include <asm/acpi.h> | 28 | #include <asm/acpi.h> |
| 29 | #include <asm/apicdef.h> | 29 | #include <asm/apicdef.h> |
| 30 | #include <asm/page.h> | 30 | #include <asm/page.h> |
| 31 | #ifdef CONFIG_HIGHMEM | ||
| 32 | #include <linux/threads.h> | 31 | #include <linux/threads.h> |
| 33 | #include <asm/kmap_types.h> | 32 | #include <asm/kmap_types.h> |
| 34 | #endif | ||
| 35 | 33 | ||
| 36 | /* | 34 | /* |
| 37 | * Here we define all the compile-time 'special' virtual | 35 | * Here we define all the compile-time 'special' virtual |
| @@ -75,10 +73,8 @@ enum fixed_addresses { | |||
| 75 | #ifdef CONFIG_X86_CYCLONE_TIMER | 73 | #ifdef CONFIG_X86_CYCLONE_TIMER |
| 76 | FIX_CYCLONE_TIMER, /*cyclone timer register*/ | 74 | FIX_CYCLONE_TIMER, /*cyclone timer register*/ |
| 77 | #endif | 75 | #endif |
| 78 | #ifdef CONFIG_HIGHMEM | ||
| 79 | FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ | 76 | FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ |
| 80 | FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, | 77 | FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, |
| 81 | #endif | ||
| 82 | #ifdef CONFIG_PCI_MMCONFIG | 78 | #ifdef CONFIG_PCI_MMCONFIG |
| 83 | FIX_PCIE_MCFG, | 79 | FIX_PCIE_MCFG, |
| 84 | #endif | 80 | #endif |
diff --git a/arch/x86/include/asm/highmem.h b/arch/x86/include/asm/highmem.h index a3b3b7c3027b..bf9276bea660 100644 --- a/arch/x86/include/asm/highmem.h +++ b/arch/x86/include/asm/highmem.h | |||
| @@ -25,14 +25,11 @@ | |||
| 25 | #include <asm/kmap_types.h> | 25 | #include <asm/kmap_types.h> |
| 26 | #include <asm/tlbflush.h> | 26 | #include <asm/tlbflush.h> |
| 27 | #include <asm/paravirt.h> | 27 | #include <asm/paravirt.h> |
| 28 | #include <asm/fixmap.h> | ||
| 28 | 29 | ||
| 29 | /* declarations for highmem.c */ | 30 | /* declarations for highmem.c */ |
| 30 | extern unsigned long highstart_pfn, highend_pfn; | 31 | extern unsigned long highstart_pfn, highend_pfn; |
| 31 | 32 | ||
| 32 | extern pte_t *kmap_pte; | ||
| 33 | extern pgprot_t kmap_prot; | ||
| 34 | extern pte_t *pkmap_page_table; | ||
| 35 | |||
| 36 | /* | 33 | /* |
| 37 | * Right now we initialize only a single pte table. It can be extended | 34 | * Right now we initialize only a single pte table. It can be extended |
| 38 | * easily, subsequent pte tables have to be allocated in one physical | 35 | * easily, subsequent pte tables have to be allocated in one physical |
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile index 59f89b434b45..fea4565ff576 100644 --- a/arch/x86/mm/Makefile +++ b/arch/x86/mm/Makefile | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | obj-y := init_$(BITS).o fault.o ioremap.o extable.o pageattr.o mmap.o \ | 1 | obj-y := init_$(BITS).o fault.o ioremap.o extable.o pageattr.o mmap.o \ |
| 2 | pat.o pgtable.o gup.o | 2 | pat.o pgtable.o gup.o |
| 3 | 3 | ||
| 4 | obj-$(CONFIG_X86_32) += pgtable_32.o | 4 | obj-$(CONFIG_X86_32) += pgtable_32.o iomap_32.o |
| 5 | 5 | ||
| 6 | obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o | 6 | obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o |
| 7 | obj-$(CONFIG_X86_PTDUMP) += dump_pagetables.o | 7 | obj-$(CONFIG_X86_PTDUMP) += dump_pagetables.o |
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 8396868e82c5..c483f4242079 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c | |||
| @@ -334,7 +334,6 @@ int devmem_is_allowed(unsigned long pagenr) | |||
| 334 | return 0; | 334 | return 0; |
| 335 | } | 335 | } |
| 336 | 336 | ||
| 337 | #ifdef CONFIG_HIGHMEM | ||
| 338 | pte_t *kmap_pte; | 337 | pte_t *kmap_pte; |
| 339 | pgprot_t kmap_prot; | 338 | pgprot_t kmap_prot; |
| 340 | 339 | ||
| @@ -357,6 +356,7 @@ static void __init kmap_init(void) | |||
| 357 | kmap_prot = PAGE_KERNEL; | 356 | kmap_prot = PAGE_KERNEL; |
| 358 | } | 357 | } |
| 359 | 358 | ||
| 359 | #ifdef CONFIG_HIGHMEM | ||
| 360 | static void __init permanent_kmaps_init(pgd_t *pgd_base) | 360 | static void __init permanent_kmaps_init(pgd_t *pgd_base) |
| 361 | { | 361 | { |
| 362 | unsigned long vaddr; | 362 | unsigned long vaddr; |
| @@ -436,7 +436,6 @@ static void __init set_highmem_pages_init(void) | |||
| 436 | #endif /* !CONFIG_NUMA */ | 436 | #endif /* !CONFIG_NUMA */ |
| 437 | 437 | ||
| 438 | #else | 438 | #else |
| 439 | # define kmap_init() do { } while (0) | ||
| 440 | # define permanent_kmaps_init(pgd_base) do { } while (0) | 439 | # define permanent_kmaps_init(pgd_base) do { } while (0) |
| 441 | # define set_highmem_pages_init() do { } while (0) | 440 | # define set_highmem_pages_init() do { } while (0) |
| 442 | #endif /* CONFIG_HIGHMEM */ | 441 | #endif /* CONFIG_HIGHMEM */ |
diff --git a/arch/x86/mm/iomap_32.c b/arch/x86/mm/iomap_32.c new file mode 100644 index 000000000000..d0151d8ce452 --- /dev/null +++ b/arch/x86/mm/iomap_32.c | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | /* | ||
| 2 | * Copyright © 2008 Ingo Molnar | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License as published by | ||
| 6 | * the Free Software Foundation; either version 2 of the License, or | ||
| 7 | * (at your option) any later version. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, but | ||
| 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 12 | * General Public License for more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along | ||
| 15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 16 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <asm/iomap.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | |||
| 22 | /* Map 'pfn' using fixed map 'type' and protections 'prot' | ||
| 23 | */ | ||
| 24 | void * | ||
| 25 | iomap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t prot) | ||
| 26 | { | ||
| 27 | enum fixed_addresses idx; | ||
| 28 | unsigned long vaddr; | ||
| 29 | |||
| 30 | pagefault_disable(); | ||
| 31 | |||
| 32 | idx = type + KM_TYPE_NR*smp_processor_id(); | ||
| 33 | vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); | ||
| 34 | set_pte(kmap_pte-idx, pfn_pte(pfn, prot)); | ||
| 35 | arch_flush_lazy_mmu_mode(); | ||
| 36 | |||
| 37 | return (void*) vaddr; | ||
| 38 | } | ||
| 39 | EXPORT_SYMBOL_GPL(iomap_atomic_prot_pfn); | ||
| 40 | |||
| 41 | void | ||
| 42 | iounmap_atomic(void *kvaddr, enum km_type type) | ||
| 43 | { | ||
| 44 | unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK; | ||
| 45 | enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id(); | ||
| 46 | |||
| 47 | /* | ||
| 48 | * Force other mappings to Oops if they'll try to access this pte | ||
| 49 | * without first remap it. Keeping stale mappings around is a bad idea | ||
| 50 | * also, in case the page changes cacheability attributes or becomes | ||
| 51 | * a protected page in a hypervisor. | ||
| 52 | */ | ||
| 53 | if (vaddr == __fix_to_virt(FIX_KMAP_BEGIN+idx)) | ||
| 54 | kpte_clear_flush(kmap_pte-idx, vaddr); | ||
| 55 | |||
| 56 | arch_flush_lazy_mmu_mode(); | ||
| 57 | pagefault_enable(); | ||
| 58 | } | ||
| 59 | EXPORT_SYMBOL_GPL(iounmap_atomic); | ||
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index cc8a9f3f7a60..572dcd0e3e0d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #define _I915_DRV_H_ | 31 | #define _I915_DRV_H_ |
| 32 | 32 | ||
| 33 | #include "i915_reg.h" | 33 | #include "i915_reg.h" |
| 34 | #include <linux/io-mapping.h> | ||
| 34 | 35 | ||
| 35 | /* General customization: | 36 | /* General customization: |
| 36 | */ | 37 | */ |
| @@ -246,6 +247,8 @@ typedef struct drm_i915_private { | |||
| 246 | struct { | 247 | struct { |
| 247 | struct drm_mm gtt_space; | 248 | struct drm_mm gtt_space; |
| 248 | 249 | ||
| 250 | struct io_mapping *gtt_mapping; | ||
| 251 | |||
| 249 | /** | 252 | /** |
| 250 | * List of objects currently involved in rendering from the | 253 | * List of objects currently involved in rendering from the |
| 251 | * ringbuffer. | 254 | * ringbuffer. |
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c1733ac4a7f5..b0ec73fa6a93 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c | |||
| @@ -193,35 +193,50 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, | |||
| 193 | return 0; | 193 | return 0; |
| 194 | } | 194 | } |
| 195 | 195 | ||
| 196 | /* | 196 | /* This is the fast write path which cannot handle |
| 197 | * Try to write quickly with an atomic kmap. Return true on success. | 197 | * page faults in the source data |
| 198 | * | ||
| 199 | * If this fails (which includes a partial write), we'll redo the whole | ||
| 200 | * thing with the slow version. | ||
| 201 | * | ||
| 202 | * This is a workaround for the low performance of iounmap (approximate | ||
| 203 | * 10% cpu cost on normal 3D workloads). kmap_atomic on HIGHMEM kernels | ||
| 204 | * happens to let us map card memory without taking IPIs. When the vmap | ||
| 205 | * rework lands we should be able to dump this hack. | ||
| 206 | */ | 198 | */ |
| 207 | static inline int fast_user_write(unsigned long pfn, char __user *user_data, | 199 | |
| 208 | int l, int o) | 200 | static inline int |
| 201 | fast_user_write(struct io_mapping *mapping, | ||
| 202 | loff_t page_base, int page_offset, | ||
| 203 | char __user *user_data, | ||
| 204 | int length) | ||
| 209 | { | 205 | { |
| 210 | #ifdef CONFIG_HIGHMEM | ||
| 211 | unsigned long unwritten; | ||
| 212 | char *vaddr_atomic; | 206 | char *vaddr_atomic; |
| 207 | unsigned long unwritten; | ||
| 213 | 208 | ||
| 214 | vaddr_atomic = kmap_atomic_pfn(pfn, KM_USER0); | 209 | vaddr_atomic = io_mapping_map_atomic_wc(mapping, page_base); |
| 215 | #if WATCH_PWRITE | 210 | unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + page_offset, |
| 216 | DRM_INFO("pwrite i %d o %d l %d pfn %ld vaddr %p\n", | 211 | user_data, length); |
| 217 | i, o, l, pfn, vaddr_atomic); | 212 | io_mapping_unmap_atomic(vaddr_atomic); |
| 218 | #endif | 213 | if (unwritten) |
| 219 | unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + o, user_data, l); | 214 | return -EFAULT; |
| 220 | kunmap_atomic(vaddr_atomic, KM_USER0); | 215 | return 0; |
| 221 | return !unwritten; | 216 | } |
| 222 | #else | 217 | |
| 218 | /* Here's the write path which can sleep for | ||
| 219 | * page faults | ||
| 220 | */ | ||
| 221 | |||
| 222 | static inline int | ||
| 223 | slow_user_write(struct io_mapping *mapping, | ||
| 224 | loff_t page_base, int page_offset, | ||
| 225 | char __user *user_data, | ||
| 226 | int length) | ||
| 227 | { | ||
| 228 | char __iomem *vaddr; | ||
| 229 | unsigned long unwritten; | ||
| 230 | |||
| 231 | vaddr = io_mapping_map_wc(mapping, page_base); | ||
| 232 | if (vaddr == NULL) | ||
| 233 | return -EFAULT; | ||
| 234 | unwritten = __copy_from_user(vaddr + page_offset, | ||
| 235 | user_data, length); | ||
| 236 | io_mapping_unmap(vaddr); | ||
| 237 | if (unwritten) | ||
| 238 | return -EFAULT; | ||
| 223 | return 0; | 239 | return 0; |
| 224 | #endif | ||
| 225 | } | 240 | } |
| 226 | 241 | ||
| 227 | static int | 242 | static int |
| @@ -230,10 +245,12 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj, | |||
| 230 | struct drm_file *file_priv) | 245 | struct drm_file *file_priv) |
| 231 | { | 246 | { |
| 232 | struct drm_i915_gem_object *obj_priv = obj->driver_private; | 247 | struct drm_i915_gem_object *obj_priv = obj->driver_private; |
| 248 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
| 233 | ssize_t remain; | 249 | ssize_t remain; |
| 234 | loff_t offset; | 250 | loff_t offset, page_base; |
| 235 | char __user *user_data; | 251 | char __user *user_data; |
| 236 | int ret = 0; | 252 | int page_offset, page_length; |
| 253 | int ret; | ||
| 237 | 254 | ||
| 238 | user_data = (char __user *) (uintptr_t) args->data_ptr; | 255 | user_data = (char __user *) (uintptr_t) args->data_ptr; |
| 239 | remain = args->size; | 256 | remain = args->size; |
| @@ -257,57 +274,37 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj, | |||
| 257 | obj_priv->dirty = 1; | 274 | obj_priv->dirty = 1; |
| 258 | 275 | ||
| 259 | while (remain > 0) { | 276 | while (remain > 0) { |
| 260 | unsigned long pfn; | ||
| 261 | int i, o, l; | ||
| 262 | |||
| 263 | /* Operation in this page | 277 | /* Operation in this page |
| 264 | * | 278 | * |
| 265 | * i = page number | 279 | * page_base = page offset within aperture |
| 266 | * o = offset within page | 280 | * page_offset = offset within page |
| 267 | * l = bytes to copy | 281 | * page_length = bytes to copy for this page |
| 268 | */ | 282 | */ |
| 269 | i = offset >> PAGE_SHIFT; | 283 | page_base = (offset & ~(PAGE_SIZE-1)); |
| 270 | o = offset & (PAGE_SIZE-1); | 284 | page_offset = offset & (PAGE_SIZE-1); |
| 271 | l = remain; | 285 | page_length = remain; |
| 272 | if ((o + l) > PAGE_SIZE) | 286 | if ((page_offset + remain) > PAGE_SIZE) |
| 273 | l = PAGE_SIZE - o; | 287 | page_length = PAGE_SIZE - page_offset; |
| 274 | 288 | ||
| 275 | pfn = (dev->agp->base >> PAGE_SHIFT) + i; | 289 | ret = fast_user_write (dev_priv->mm.gtt_mapping, page_base, |
| 276 | 290 | page_offset, user_data, page_length); | |
| 277 | if (!fast_user_write(pfn, user_data, l, o)) { | 291 | |
| 278 | unsigned long unwritten; | 292 | /* If we get a fault while copying data, then (presumably) our |
| 279 | char __iomem *vaddr; | 293 | * source page isn't available. In this case, use the |
| 280 | 294 | * non-atomic function | |
| 281 | vaddr = ioremap_wc(pfn << PAGE_SHIFT, PAGE_SIZE); | 295 | */ |
| 282 | #if WATCH_PWRITE | 296 | if (ret) { |
| 283 | DRM_INFO("pwrite slow i %d o %d l %d " | 297 | ret = slow_user_write (dev_priv->mm.gtt_mapping, |
| 284 | "pfn %ld vaddr %p\n", | 298 | page_base, page_offset, |
| 285 | i, o, l, pfn, vaddr); | 299 | user_data, page_length); |
| 286 | #endif | 300 | if (ret) |
| 287 | if (vaddr == NULL) { | ||
| 288 | ret = -EFAULT; | ||
| 289 | goto fail; | ||
| 290 | } | ||
| 291 | unwritten = __copy_from_user(vaddr + o, user_data, l); | ||
| 292 | #if WATCH_PWRITE | ||
| 293 | DRM_INFO("unwritten %ld\n", unwritten); | ||
| 294 | #endif | ||
| 295 | iounmap(vaddr); | ||
| 296 | if (unwritten) { | ||
| 297 | ret = -EFAULT; | ||
| 298 | goto fail; | 301 | goto fail; |
| 299 | } | ||
| 300 | } | 302 | } |
| 301 | 303 | ||
| 302 | remain -= l; | 304 | remain -= page_length; |
| 303 | user_data += l; | 305 | user_data += page_length; |
| 304 | offset += l; | 306 | offset += page_length; |
| 305 | } | 307 | } |
| 306 | #if WATCH_PWRITE && 1 | ||
| 307 | i915_gem_clflush_object(obj); | ||
| 308 | i915_gem_dump_object(obj, args->offset + args->size, __func__, ~0); | ||
| 309 | i915_gem_clflush_object(obj); | ||
| 310 | #endif | ||
| 311 | 308 | ||
| 312 | fail: | 309 | fail: |
| 313 | i915_gem_object_unpin(obj); | 310 | i915_gem_object_unpin(obj); |
| @@ -1525,12 +1522,12 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, | |||
| 1525 | struct drm_i915_gem_exec_object *entry) | 1522 | struct drm_i915_gem_exec_object *entry) |
| 1526 | { | 1523 | { |
| 1527 | struct drm_device *dev = obj->dev; | 1524 | struct drm_device *dev = obj->dev; |
| 1525 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
| 1528 | struct drm_i915_gem_relocation_entry reloc; | 1526 | struct drm_i915_gem_relocation_entry reloc; |
| 1529 | struct drm_i915_gem_relocation_entry __user *relocs; | 1527 | struct drm_i915_gem_relocation_entry __user *relocs; |
| 1530 | struct drm_i915_gem_object *obj_priv = obj->driver_private; | 1528 | struct drm_i915_gem_object *obj_priv = obj->driver_private; |
| 1531 | int i, ret; | 1529 | int i, ret; |
| 1532 | uint32_t last_reloc_offset = -1; | 1530 | void __iomem *reloc_page; |
| 1533 | void __iomem *reloc_page = NULL; | ||
| 1534 | 1531 | ||
| 1535 | /* Choose the GTT offset for our buffer and put it there. */ | 1532 | /* Choose the GTT offset for our buffer and put it there. */ |
| 1536 | ret = i915_gem_object_pin(obj, (uint32_t) entry->alignment); | 1533 | ret = i915_gem_object_pin(obj, (uint32_t) entry->alignment); |
| @@ -1653,26 +1650,11 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, | |||
| 1653 | * perform. | 1650 | * perform. |
| 1654 | */ | 1651 | */ |
| 1655 | reloc_offset = obj_priv->gtt_offset + reloc.offset; | 1652 | reloc_offset = obj_priv->gtt_offset + reloc.offset; |
| 1656 | if (reloc_page == NULL || | 1653 | reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, |
| 1657 | (last_reloc_offset & ~(PAGE_SIZE - 1)) != | 1654 | (reloc_offset & |
| 1658 | (reloc_offset & ~(PAGE_SIZE - 1))) { | 1655 | ~(PAGE_SIZE - 1))); |
| 1659 | if (reloc_page != NULL) | ||
| 1660 | iounmap(reloc_page); | ||
| 1661 | |||
| 1662 | reloc_page = ioremap_wc(dev->agp->base + | ||
| 1663 | (reloc_offset & | ||
| 1664 | ~(PAGE_SIZE - 1)), | ||
| 1665 | PAGE_SIZE); | ||
| 1666 | last_reloc_offset = reloc_offset; | ||
| 1667 | if (reloc_page == NULL) { | ||
| 1668 | drm_gem_object_unreference(target_obj); | ||
| 1669 | i915_gem_object_unpin(obj); | ||
| 1670 | return -ENOMEM; | ||
| 1671 | } | ||
| 1672 | } | ||
| 1673 | |||
| 1674 | reloc_entry = (uint32_t __iomem *)(reloc_page + | 1656 | reloc_entry = (uint32_t __iomem *)(reloc_page + |
| 1675 | (reloc_offset & (PAGE_SIZE - 1))); | 1657 | (reloc_offset & (PAGE_SIZE - 1))); |
| 1676 | reloc_val = target_obj_priv->gtt_offset + reloc.delta; | 1658 | reloc_val = target_obj_priv->gtt_offset + reloc.delta; |
| 1677 | 1659 | ||
| 1678 | #if WATCH_BUF | 1660 | #if WATCH_BUF |
| @@ -1681,6 +1663,7 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, | |||
| 1681 | readl(reloc_entry), reloc_val); | 1663 | readl(reloc_entry), reloc_val); |
| 1682 | #endif | 1664 | #endif |
| 1683 | writel(reloc_val, reloc_entry); | 1665 | writel(reloc_val, reloc_entry); |
| 1666 | io_mapping_unmap_atomic(reloc_page); | ||
| 1684 | 1667 | ||
| 1685 | /* Write the updated presumed offset for this entry back out | 1668 | /* Write the updated presumed offset for this entry back out |
| 1686 | * to the user. | 1669 | * to the user. |
| @@ -1696,9 +1679,6 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, | |||
| 1696 | drm_gem_object_unreference(target_obj); | 1679 | drm_gem_object_unreference(target_obj); |
| 1697 | } | 1680 | } |
| 1698 | 1681 | ||
| 1699 | if (reloc_page != NULL) | ||
| 1700 | iounmap(reloc_page); | ||
| 1701 | |||
| 1702 | #if WATCH_BUF | 1682 | #if WATCH_BUF |
| 1703 | if (0) | 1683 | if (0) |
| 1704 | i915_gem_dump_object(obj, 128, __func__, ~0); | 1684 | i915_gem_dump_object(obj, 128, __func__, ~0); |
| @@ -2540,6 +2520,10 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, | |||
| 2540 | if (ret != 0) | 2520 | if (ret != 0) |
| 2541 | return ret; | 2521 | return ret; |
| 2542 | 2522 | ||
| 2523 | dev_priv->mm.gtt_mapping = io_mapping_create_wc(dev->agp->base, | ||
| 2524 | dev->agp->agp_info.aper_size | ||
| 2525 | * 1024 * 1024); | ||
| 2526 | |||
| 2543 | mutex_lock(&dev->struct_mutex); | 2527 | mutex_lock(&dev->struct_mutex); |
| 2544 | BUG_ON(!list_empty(&dev_priv->mm.active_list)); | 2528 | BUG_ON(!list_empty(&dev_priv->mm.active_list)); |
| 2545 | BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); | 2529 | BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); |
| @@ -2557,11 +2541,13 @@ int | |||
| 2557 | i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, | 2541 | i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, |
| 2558 | struct drm_file *file_priv) | 2542 | struct drm_file *file_priv) |
| 2559 | { | 2543 | { |
| 2544 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
| 2560 | int ret; | 2545 | int ret; |
| 2561 | 2546 | ||
| 2562 | ret = i915_gem_idle(dev); | 2547 | ret = i915_gem_idle(dev); |
| 2563 | drm_irq_uninstall(dev); | 2548 | drm_irq_uninstall(dev); |
| 2564 | 2549 | ||
| 2550 | io_mapping_free(dev_priv->mm.gtt_mapping); | ||
| 2565 | return ret; | 2551 | return ret; |
| 2566 | } | 2552 | } |
| 2567 | 2553 | ||
diff --git a/include/asm-x86/iomap.h b/include/asm-x86/iomap.h new file mode 100644 index 000000000000..c1f06289b14b --- /dev/null +++ b/include/asm-x86/iomap.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* | ||
| 2 | * Copyright © 2008 Ingo Molnar | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License as published by | ||
| 6 | * the Free Software Foundation; either version 2 of the License, or | ||
| 7 | * (at your option) any later version. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, but | ||
| 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 12 | * General Public License for more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along | ||
| 15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 16 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/fs.h> | ||
| 20 | #include <linux/mm.h> | ||
| 21 | #include <linux/uaccess.h> | ||
| 22 | #include <asm/cacheflush.h> | ||
| 23 | #include <asm/pgtable.h> | ||
| 24 | #include <asm/tlbflush.h> | ||
| 25 | |||
| 26 | void * | ||
| 27 | iomap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t prot); | ||
| 28 | |||
| 29 | void | ||
| 30 | iounmap_atomic(void *kvaddr, enum km_type type); | ||
diff --git a/include/linux/io-mapping.h b/include/linux/io-mapping.h new file mode 100644 index 000000000000..82df31726a54 --- /dev/null +++ b/include/linux/io-mapping.h | |||
| @@ -0,0 +1,125 @@ | |||
| 1 | /* | ||
| 2 | * Copyright © 2008 Keith Packard <keithp@keithp.com> | ||
| 3 | * | ||
| 4 | * This file is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of version 2 of the GNU General Public License | ||
| 6 | * as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License | ||
| 14 | * along with this program; if not, write to the Free Software Foundation, | ||
| 15 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef _LINUX_IO_MAPPING_H | ||
| 19 | #define _LINUX_IO_MAPPING_H | ||
| 20 | |||
| 21 | #include <linux/types.h> | ||
| 22 | #include <asm/io.h> | ||
| 23 | #include <asm/page.h> | ||
| 24 | #include <asm/iomap.h> | ||
| 25 | |||
| 26 | /* | ||
| 27 | * The io_mapping mechanism provides an abstraction for mapping | ||
| 28 | * individual pages from an io device to the CPU in an efficient fashion. | ||
| 29 | * | ||
| 30 | * See Documentation/io_mapping.txt | ||
| 31 | */ | ||
| 32 | |||
| 33 | /* this struct isn't actually defined anywhere */ | ||
| 34 | struct io_mapping; | ||
| 35 | |||
| 36 | #ifdef CONFIG_HAVE_ATOMIC_IOMAP | ||
| 37 | |||
| 38 | /* | ||
| 39 | * For small address space machines, mapping large objects | ||
| 40 | * into the kernel virtual space isn't practical. Where | ||
| 41 | * available, use fixmap support to dynamically map pages | ||
| 42 | * of the object at run time. | ||
| 43 | */ | ||
| 44 | |||
| 45 | static inline struct io_mapping * | ||
| 46 | io_mapping_create_wc(unsigned long base, unsigned long size) | ||
| 47 | { | ||
| 48 | return (struct io_mapping *) base; | ||
| 49 | } | ||
| 50 | |||
| 51 | static inline void | ||
| 52 | io_mapping_free(struct io_mapping *mapping) | ||
| 53 | { | ||
| 54 | } | ||
| 55 | |||
| 56 | /* Atomic map/unmap */ | ||
| 57 | static inline void * | ||
| 58 | io_mapping_map_atomic_wc(struct io_mapping *mapping, unsigned long offset) | ||
| 59 | { | ||
| 60 | offset += (unsigned long) mapping; | ||
| 61 | return iomap_atomic_prot_pfn(offset >> PAGE_SHIFT, KM_USER0, | ||
| 62 | __pgprot(__PAGE_KERNEL_WC)); | ||
| 63 | } | ||
| 64 | |||
| 65 | static inline void | ||
| 66 | io_mapping_unmap_atomic(void *vaddr) | ||
| 67 | { | ||
| 68 | iounmap_atomic(vaddr, KM_USER0); | ||
| 69 | } | ||
| 70 | |||
| 71 | static inline void * | ||
| 72 | io_mapping_map_wc(struct io_mapping *mapping, unsigned long offset) | ||
| 73 | { | ||
| 74 | offset += (unsigned long) mapping; | ||
| 75 | return ioremap_wc(offset, PAGE_SIZE); | ||
| 76 | } | ||
| 77 | |||
| 78 | static inline void | ||
| 79 | io_mapping_unmap(void *vaddr) | ||
| 80 | { | ||
| 81 | iounmap(vaddr); | ||
| 82 | } | ||
| 83 | |||
| 84 | #else | ||
| 85 | |||
| 86 | /* Create the io_mapping object*/ | ||
| 87 | static inline struct io_mapping * | ||
| 88 | io_mapping_create_wc(unsigned long base, unsigned long size) | ||
| 89 | { | ||
| 90 | return (struct io_mapping *) ioremap_wc(base, size); | ||
| 91 | } | ||
| 92 | |||
| 93 | static inline void | ||
| 94 | io_mapping_free(struct io_mapping *mapping) | ||
| 95 | { | ||
| 96 | iounmap(mapping); | ||
| 97 | } | ||
| 98 | |||
| 99 | /* Atomic map/unmap */ | ||
| 100 | static inline void * | ||
| 101 | io_mapping_map_atomic_wc(struct io_mapping *mapping, unsigned long offset) | ||
| 102 | { | ||
| 103 | return ((char *) mapping) + offset; | ||
| 104 | } | ||
| 105 | |||
| 106 | static inline void | ||
| 107 | io_mapping_unmap_atomic(void *vaddr) | ||
| 108 | { | ||
| 109 | } | ||
| 110 | |||
| 111 | /* Non-atomic map/unmap */ | ||
| 112 | static inline void * | ||
| 113 | io_mapping_map_wc(struct io_mapping *mapping, unsigned long offset) | ||
| 114 | { | ||
| 115 | return ((char *) mapping) + offset; | ||
| 116 | } | ||
| 117 | |||
| 118 | static inline void | ||
| 119 | io_mapping_unmap(void *vaddr) | ||
| 120 | { | ||
| 121 | } | ||
| 122 | |||
| 123 | #endif /* HAVE_ATOMIC_IOMAP */ | ||
| 124 | |||
| 125 | #endif /* _LINUX_IO_MAPPING_H */ | ||
