summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkettenis <kettenis@openbsd.org>2014-09-20 21:17:43 +0000
committerkettenis <kettenis@openbsd.org>2014-09-20 21:17:43 +0000
commit192a53d1e331aec22d290c945a2ee76397582995 (patch)
treecfab7552c787699d1fb9f4a7076c3ba00f4c24a2
parentprovide a hint for enabling jumbo frames, since we removed it from the (diff)
downloadwireguard-openbsd-192a53d1e331aec22d290c945a2ee76397582995.tar.xz
wireguard-openbsd-192a53d1e331aec22d290c945a2ee76397582995.zip
Make another fast path properly atomic.
-rw-r--r--sys/dev/pci/drm/drm_linux.h4
-rw-r--r--sys/dev/pci/drm/i915/i915_gem.c77
2 files changed, 61 insertions, 20 deletions
diff --git a/sys/dev/pci/drm/drm_linux.h b/sys/dev/pci/drm/drm_linux.h
index e341f86d720..81b7012de44 100644
--- a/sys/dev/pci/drm/drm_linux.h
+++ b/sys/dev/pci/drm/drm_linux.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: drm_linux.h,v 1.1 2014/04/01 20:16:50 kettenis Exp $ */
+/* $OpenBSD: drm_linux.h,v 1.2 2014/09/20 21:17:43 kettenis Exp $ */
/*
* Copyright (c) 2013, 2014 Mark Kettenis
*
@@ -15,6 +15,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define __force
+
#if defined(__i386__) || defined(__amd64__)
static inline void
diff --git a/sys/dev/pci/drm/i915/i915_gem.c b/sys/dev/pci/drm/i915/i915_gem.c
index 6ec8b65327c..150fee67be2 100644
--- a/sys/dev/pci/drm/i915/i915_gem.c
+++ b/sys/dev/pci/drm/i915/i915_gem.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: i915_gem.c,v 1.74 2014/07/12 18:48:52 tedu Exp $ */
+/* $OpenBSD: i915_gem.c,v 1.75 2014/09/20 21:17:43 kettenis Exp $ */
/*
* Copyright (c) 2008-2009 Owain G. Ainsworth <oga@openbsd.org>
*
@@ -696,6 +696,31 @@ fast_user_write(struct io_mapping *mapping,
io_mapping_unmap_atomic(vaddr_atomic);
return unwritten;
}
+#else
+/* This is the fast write path which cannot handle
+ * page faults in the source data
+ */
+
+static inline int
+fast_user_write(struct drm_i915_private *dev_priv,
+ bus_size_t page_base, int page_offset,
+ char __user *user_data,
+ int length)
+{
+ bus_space_handle_t bsh;
+ void __iomem *vaddr_atomic;
+ void *vaddr;
+ unsigned long unwritten;
+
+ agp_map_atomic(dev_priv->agph, page_base, &bsh);
+ vaddr_atomic = bus_space_vaddr(dev_priv->bst, bsh);
+ /* We can use the cpu mem copy function because this is X86. */
+ vaddr = (void __force*)vaddr_atomic + page_offset;
+ unwritten = __copy_from_user_inatomic_nocache(vaddr,
+ user_data, length);
+ agp_unmap_atomic(dev_priv->agph, bsh);
+ return unwritten;
+}
#endif
/**
@@ -709,11 +734,10 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev,
struct drm_file *file)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- bus_space_handle_t bsh;
- bus_addr_t offset;
- bus_size_t size;
- char *vaddr;
- int ret;
+ ssize_t remain;
+ bus_size_t offset, page_base;
+ char __user *user_data;
+ int page_offset, page_length, ret;
ret = i915_gem_object_pin(obj, 0, true, true);
if (ret)
@@ -727,23 +751,38 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev,
if (ret)
goto out_unpin;
+ user_data = (char __user *) (uintptr_t) args->data_ptr;
+ remain = args->size;
+
offset = obj->gtt_offset + args->offset;
- size = round_page(offset + args->size) - trunc_page(offset);
- if ((ret = agp_map_subregion(dev_priv->agph,
- trunc_page(offset), size, &bsh)) != 0)
- goto out_unpin;
- vaddr = bus_space_vaddr(dev_priv->bst, bsh);
- if (vaddr == NULL) {
- ret = -EFAULT;
- goto out_unmap;
- }
+ while (remain > 0) {
+ /* Operation in this page
+ *
+ * page_base = page offset within aperture
+ * page_offset = offset within page
+ * page_length = bytes to copy for this page
+ */
+ page_base = offset & ~PAGE_MASK;
+ page_offset = offset_in_page(offset);
+ page_length = remain;
+ if ((page_offset + remain) > PAGE_SIZE)
+ page_length = PAGE_SIZE - page_offset;
- ret = -copyin((char *)(uintptr_t)args->data_ptr,
- vaddr + (offset & PAGE_MASK), args->size);
+ /* If we get a fault while copying data, then (presumably) our
+ * source page isn't available. Return the error and we'll
+ * retry in the slow path.
+ */
+ if (fast_user_write(dev_priv, page_base,
+ page_offset, user_data, page_length)) {
+ ret = -EFAULT;
+ goto out_unpin;
+ }
-out_unmap:
- agp_unmap_subregion(dev_priv->agph, bsh, size);
+ remain -= page_length;
+ user_data += page_length;
+ offset += page_length;
+ }
out_unpin:
i915_gem_object_unpin(obj);