aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-11-03 13:15:40 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2008-11-03 13:15:40 -0500
commitda4a22cba7cb2d922691214aed6b1977f04efaff (patch)
tree89d3f02b13cd1eb280a33240878880f91066bac2
parent20ebc0073b0fb63ce4a27ca761418ecfdecaadb7 (diff)
parente5beae16901795223d677f15aa2fe192976278ee (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.txt82
-rw-r--r--arch/x86/Kconfig4
-rw-r--r--arch/x86/include/asm/fixmap.h4
-rw-r--r--arch/x86/include/asm/fixmap_32.h4
-rw-r--r--arch/x86/include/asm/highmem.h5
-rw-r--r--arch/x86/mm/Makefile2
-rw-r--r--arch/x86/mm/init_32.c3
-rw-r--r--arch/x86/mm/iomap_32.c59
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h3
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c174
-rw-r--r--include/asm-x86/iomap.h30
-rw-r--r--include/linux/io-mapping.h125
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 @@
1The io_mapping functions in linux/io-mapping.h provide an abstraction for
2efficiently mapping small regions of an I/O device to the CPU. The initial
3usage is to support the large graphics aperture on 32-bit processors where
4ioremap_wc cannot be used to statically map the entire aperture to the CPU
5as it would consume too much of the kernel address space.
6
7A 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
19With this mapping object, individual pages can be mapped either atomically
20or not, depending on the necessary scheduling environment. Of course, atomic
21maps 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
45If you need to sleep while holding the lock, you can use the non-atomic
46variant, 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
59At driver close time, the io_mapping object must be freed:
60
61 void io_mapping_free(struct io_mapping *mapping)
62
63Current Implementation:
64
65The initial implementation of these functions uses existing mapping
66mechanisms and so provides only an abstraction layer and no new
67functionality.
68
69On 64-bit processors, io_mapping_create_wc calls ioremap_wc for the whole
70range, creating a permanent kernel-visible mapping to the resource. The
71map_atomic and map functions add the requested offset to the base of the
72virtual address returned by ioremap_wc.
73
74On 32-bit processors with HIGHMEM defined, io_mapping_map_atomic_wc uses
75kmap_atomic_pfn to map the specified page in an atomic fashion;
76kmap_atomic_pfn isn't really supposed to be used with device pages, but it
77provides an efficient mapping for this usage.
78
79On 32-bit processors without HIGHMEM defined, io_mapping_map_atomic_wc and
80io_mapping_map_wc both use ioremap_wc, a terribly inefficient function which
81performs an IPI to inform all processors about the new mapping. This results
82in 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
1894endmenu 1894endmenu
1895 1895
1896 1896
1897config HAVE_ATOMIC_IOMAP
1898 def_bool y
1899 depends on X86_32
1900
1897source "net/Kconfig" 1901source "net/Kconfig"
1898 1902
1899source "drivers/Kconfig" 1903source "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
10extern int fixmaps_set; 10extern int fixmaps_set;
11 11
12extern pte_t *kmap_pte;
13extern pgprot_t kmap_prot;
14extern pte_t *pkmap_page_table;
15
12void __native_set_fixmap(enum fixed_addresses idx, pte_t pte); 16void __native_set_fixmap(enum fixed_addresses idx, pte_t pte);
13void native_set_fixmap(enum fixed_addresses idx, 17void 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 */
30extern unsigned long highstart_pfn, highend_pfn; 31extern unsigned long highstart_pfn, highend_pfn;
31 32
32extern pte_t *kmap_pte;
33extern pgprot_t kmap_prot;
34extern 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 @@
1obj-y := init_$(BITS).o fault.o ioremap.o extable.o pageattr.o mmap.o \ 1obj-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
4obj-$(CONFIG_X86_32) += pgtable_32.o 4obj-$(CONFIG_X86_32) += pgtable_32.o iomap_32.o
5 5
6obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o 6obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
7obj-$(CONFIG_X86_PTDUMP) += dump_pagetables.o 7obj-$(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
338pte_t *kmap_pte; 337pte_t *kmap_pte;
339pgprot_t kmap_prot; 338pgprot_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
360static void __init permanent_kmaps_init(pgd_t *pgd_base) 360static 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 */
24void *
25iomap_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}
39EXPORT_SYMBOL_GPL(iomap_atomic_prot_pfn);
40
41void
42iounmap_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}
59EXPORT_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 */
207static inline int fast_user_write(unsigned long pfn, char __user *user_data, 199
208 int l, int o) 200static inline int
201fast_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
222static inline int
223slow_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
227static int 242static 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
312fail: 309fail:
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
2557i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, 2541i915_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
26void *
27iomap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t prot);
28
29void
30iounmap_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 */
34struct 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
45static inline struct io_mapping *
46io_mapping_create_wc(unsigned long base, unsigned long size)
47{
48 return (struct io_mapping *) base;
49}
50
51static inline void
52io_mapping_free(struct io_mapping *mapping)
53{
54}
55
56/* Atomic map/unmap */
57static inline void *
58io_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
65static inline void
66io_mapping_unmap_atomic(void *vaddr)
67{
68 iounmap_atomic(vaddr, KM_USER0);
69}
70
71static inline void *
72io_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
78static inline void
79io_mapping_unmap(void *vaddr)
80{
81 iounmap(vaddr);
82}
83
84#else
85
86/* Create the io_mapping object*/
87static inline struct io_mapping *
88io_mapping_create_wc(unsigned long base, unsigned long size)
89{
90 return (struct io_mapping *) ioremap_wc(base, size);
91}
92
93static inline void
94io_mapping_free(struct io_mapping *mapping)
95{
96 iounmap(mapping);
97}
98
99/* Atomic map/unmap */
100static inline void *
101io_mapping_map_atomic_wc(struct io_mapping *mapping, unsigned long offset)
102{
103 return ((char *) mapping) + offset;
104}
105
106static inline void
107io_mapping_unmap_atomic(void *vaddr)
108{
109}
110
111/* Non-atomic map/unmap */
112static inline void *
113io_mapping_map_wc(struct io_mapping *mapping, unsigned long offset)
114{
115 return ((char *) mapping) + offset;
116}
117
118static inline void
119io_mapping_unmap(void *vaddr)
120{
121}
122
123#endif /* HAVE_ATOMIC_IOMAP */
124
125#endif /* _LINUX_IO_MAPPING_H */