diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_gem.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_gem.c | 60 |
1 files changed, 37 insertions, 23 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 9ac73dd1b422..dc2e6fdb6ca3 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c | |||
@@ -171,6 +171,37 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, | |||
171 | return 0; | 171 | return 0; |
172 | } | 172 | } |
173 | 173 | ||
174 | /* | ||
175 | * Try to write quickly with an atomic kmap. Return true on success. | ||
176 | * | ||
177 | * If this fails (which includes a partial write), we'll redo the whole | ||
178 | * thing with the slow version. | ||
179 | * | ||
180 | * This is a workaround for the low performance of iounmap (approximate | ||
181 | * 10% cpu cost on normal 3D workloads). kmap_atomic on HIGHMEM kernels | ||
182 | * happens to let us map card memory without taking IPIs. When the vmap | ||
183 | * rework lands we should be able to dump this hack. | ||
184 | */ | ||
185 | static inline int fast_user_write(unsigned long pfn, char __user *user_data, | ||
186 | int l, int o) | ||
187 | { | ||
188 | #ifdef CONFIG_HIGHMEM | ||
189 | unsigned long unwritten; | ||
190 | char *vaddr_atomic; | ||
191 | |||
192 | vaddr_atomic = kmap_atomic_pfn(pfn, KM_USER0); | ||
193 | #if WATCH_PWRITE | ||
194 | DRM_INFO("pwrite i %d o %d l %d pfn %ld vaddr %p\n", | ||
195 | i, o, l, pfn, vaddr_atomic); | ||
196 | #endif | ||
197 | unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + o, user_data, l); | ||
198 | kunmap_atomic(vaddr_atomic, KM_USER0); | ||
199 | return !unwritten; | ||
200 | #else | ||
201 | return 0; | ||
202 | #endif | ||
203 | } | ||
204 | |||
174 | static int | 205 | static int |
175 | i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj, | 206 | i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj, |
176 | struct drm_i915_gem_pwrite *args, | 207 | struct drm_i915_gem_pwrite *args, |
@@ -180,12 +211,7 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj, | |||
180 | ssize_t remain; | 211 | ssize_t remain; |
181 | loff_t offset; | 212 | loff_t offset; |
182 | char __user *user_data; | 213 | char __user *user_data; |
183 | char __iomem *vaddr; | ||
184 | char *vaddr_atomic; | ||
185 | int i, o, l; | ||
186 | int ret = 0; | 214 | int ret = 0; |
187 | unsigned long pfn; | ||
188 | unsigned long unwritten; | ||
189 | 215 | ||
190 | user_data = (char __user *) (uintptr_t) args->data_ptr; | 216 | user_data = (char __user *) (uintptr_t) args->data_ptr; |
191 | remain = args->size; | 217 | remain = args->size; |
@@ -209,6 +235,9 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj, | |||
209 | obj_priv->dirty = 1; | 235 | obj_priv->dirty = 1; |
210 | 236 | ||
211 | while (remain > 0) { | 237 | while (remain > 0) { |
238 | unsigned long pfn; | ||
239 | int i, o, l; | ||
240 | |||
212 | /* Operation in this page | 241 | /* Operation in this page |
213 | * | 242 | * |
214 | * i = page number | 243 | * i = page number |
@@ -223,25 +252,10 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj, | |||
223 | 252 | ||
224 | pfn = (dev->agp->base >> PAGE_SHIFT) + i; | 253 | pfn = (dev->agp->base >> PAGE_SHIFT) + i; |
225 | 254 | ||
226 | #ifdef CONFIG_HIGHMEM | 255 | if (!fast_user_write(pfn, user_data, l, o)) { |
227 | /* This is a workaround for the low performance of iounmap | 256 | unsigned long unwritten; |
228 | * (approximate 10% cpu cost on normal 3D workloads). | 257 | char __iomem *vaddr; |
229 | * kmap_atomic on HIGHMEM kernels happens to let us map card | ||
230 | * memory without taking IPIs. When the vmap rework lands | ||
231 | * we should be able to dump this hack. | ||
232 | */ | ||
233 | vaddr_atomic = kmap_atomic_pfn(pfn, KM_USER0); | ||
234 | #if WATCH_PWRITE | ||
235 | DRM_INFO("pwrite i %d o %d l %d pfn %ld vaddr %p\n", | ||
236 | i, o, l, pfn, vaddr_atomic); | ||
237 | #endif | ||
238 | unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + o, | ||
239 | user_data, l); | ||
240 | kunmap_atomic(vaddr_atomic, KM_USER0); | ||
241 | 258 | ||
242 | if (unwritten) | ||
243 | #endif /* CONFIG_HIGHMEM */ | ||
244 | { | ||
245 | vaddr = ioremap_wc(pfn << PAGE_SHIFT, PAGE_SIZE); | 259 | vaddr = ioremap_wc(pfn << PAGE_SHIFT, PAGE_SIZE); |
246 | #if WATCH_PWRITE | 260 | #if WATCH_PWRITE |
247 | DRM_INFO("pwrite slow i %d o %d l %d " | 261 | DRM_INFO("pwrite slow i %d o %d l %d " |