diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-11-03 13:15:40 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-11-03 13:15:40 -0500 |
commit | da4a22cba7cb2d922691214aed6b1977f04efaff (patch) | |
tree | 89d3f02b13cd1eb280a33240878880f91066bac2 | |
parent | 20ebc0073b0fb63ce4a27ca761418ecfdecaadb7 (diff) | |
parent | e5beae16901795223d677f15aa2fe192976278ee (diff) |
Merge branch 'io-mappings-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'io-mappings-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
io mapping: clean up #ifdefs
io mapping: improve documentation
i915: use io-mapping interfaces instead of a variety of mapping kludges
resources: add io-mapping functions to dynamically map large device apertures
x86: add iomap_atomic*()/iounmap_atomic() on 32-bit using fixmaps
-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 */ | ||