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 /drivers/gpu/drm/i915 | |
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
Diffstat (limited to 'drivers/gpu/drm/i915')
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_gem.c | 174 |
2 files changed, 83 insertions, 94 deletions
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 | ||