All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/4] Unified VMA Offset Manager v3
@ 2013-07-17 18:14 David Herrmann
  2013-07-17 18:14 ` [PATCH v3 1/4] drm: add unified vma offset manager David Herrmann
                   ` (4 more replies)
  0 siblings, 5 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-17 18:14 UTC (permalink / raw)
  To: dri-devel; +Cc: Dave Airlie

Hi

This is v3 of the unified VMA offset manager. It merges the GEM and TTM mmap
offset managers into a unified implementation.

v2 is available here:
  http://lists.freedesktop.org/archives/dri-devel/2013-July/041222.html

Changes since v2:
 - also fix tegra to use the new manager

I intentionally excluded the access-management patches from this series. Please
see v2 for information on these. I will resend them once this series gets
merged.

Comments welcome!
Cheers
David

David Herrmann (4):
  drm: add unified vma offset manager
  drm/gem: convert to new unified vma manager
  drm/ttm: convert to unified vma offset manager
  drm/vma: provide drm_vma_node_unmap() helper

 Documentation/DocBook/drm.tmpl             |   6 +
 drivers/gpu/drm/Makefile                   |   2 +-
 drivers/gpu/drm/ast/ast_main.c             |   2 +-
 drivers/gpu/drm/cirrus/cirrus_main.c       |   2 +-
 drivers/gpu/drm/drm_gem.c                  |  90 ++--------
 drivers/gpu/drm/drm_gem_cma_helper.c       |   9 +-
 drivers/gpu/drm/drm_vma_manager.c          | 260 +++++++++++++++++++++++++++++
 drivers/gpu/drm/exynos/exynos_drm_gem.c    |   7 +-
 drivers/gpu/drm/gma500/gem.c               |   8 +-
 drivers/gpu/drm/i915/i915_gem.c            |  13 +-
 drivers/gpu/drm/mgag200/mgag200_main.c     |   2 +-
 drivers/gpu/drm/nouveau/nouveau_display.c  |   2 +-
 drivers/gpu/drm/nouveau/nouveau_gem.c      |   2 +-
 drivers/gpu/drm/omapdrm/omap_gem.c         |  11 +-
 drivers/gpu/drm/omapdrm/omap_gem_helpers.c |  49 +-----
 drivers/gpu/drm/qxl/qxl_object.h           |   2 +-
 drivers/gpu/drm/qxl/qxl_release.c          |   2 +-
 drivers/gpu/drm/radeon/radeon_object.h     |   5 +-
 drivers/gpu/drm/ttm/ttm_bo.c               |  79 +--------
 drivers/gpu/drm/ttm/ttm_bo_util.c          |   3 +-
 drivers/gpu/drm/ttm/ttm_bo_vm.c            |  81 ++++-----
 drivers/gpu/drm/udl/udl_gem.c              |   6 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_resource.c   |   4 +-
 drivers/gpu/host1x/drm/gem.c               |   4 +-
 include/drm/drmP.h                         |   7 +-
 include/drm/drm_vma_manager.h              | 154 +++++++++++++++++
 include/drm/ttm/ttm_bo_api.h               |  15 +-
 include/drm/ttm/ttm_bo_driver.h            |   7 +-
 include/uapi/drm/drm.h                     |   2 +-
 29 files changed, 530 insertions(+), 306 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_vma_manager.c
 create mode 100644 include/drm/drm_vma_manager.h

-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v3 1/4] drm: add unified vma offset manager
  2013-07-17 18:14 [PATCH v3 0/4] Unified VMA Offset Manager v3 David Herrmann
@ 2013-07-17 18:14 ` David Herrmann
  2013-07-17 18:14 ` [PATCH v3 2/4] drm/gem: convert to new unified vma manager David Herrmann
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-17 18:14 UTC (permalink / raw)
  To: dri-devel; +Cc: Dave Airlie

If we want to map GPU memory into user-space, we need to linearize the
addresses to not confuse mm-core. Currently, GEM and TTM both implement
their own offset-managers to assign a pgoff to each object for user-space
CPU access. GEM uses a hash-table, TTM uses an rbtree.

This patch provides a unified implementation that can be used to replace
both. TTM allows partial mmaps with a given offset, so we cannot use
hashtables as the start address may not be known at mmap time. Hence, we
use the rbtree-implementation of TTM.

We could easily update drm_mm to use an rbtree instead of a linked list
for it's object list and thus drop the rbtree from the vma-manager.
However, this would slow down drm_mm object allocation for all other
use-cases (rbtree insertion) and add another 4-8 bytes to each mm node.
Hence, use the separate tree but allow for later migration.

This is a rewrite of the 2012-proposal by David Airlie <airlied@linux.ie>

v2:
 - fix Docbook integration
 - drop drm_mm_node_linked() and use drm_mm_node_allocated()
 - remove unjustified likely/unlikely usage (but keep for rbtree paths)
 - remove BUG_ON() as drm_mm already does that
 - clarify page-based vs. byte-based addresses
 - use drm_vma_node_reset() for initialization, too

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 Documentation/DocBook/drm.tmpl    |   6 +
 drivers/gpu/drm/Makefile          |   2 +-
 drivers/gpu/drm/drm_vma_manager.c | 260 ++++++++++++++++++++++++++++++++++++++
 include/drm/drm_vma_manager.h     | 138 ++++++++++++++++++++
 4 files changed, 405 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/drm_vma_manager.c
 create mode 100644 include/drm/drm_vma_manager.h

diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl
index 4d54ac8..b02732d 100644
--- a/Documentation/DocBook/drm.tmpl
+++ b/Documentation/DocBook/drm.tmpl
@@ -2212,6 +2212,12 @@ void intel_crt_init(struct drm_device *dev)
 !Iinclude/drm/drm_rect.h
 !Edrivers/gpu/drm/drm_rect.c
     </sect2>
+    <sect2>
+      <title>VMA Offset Manager</title>
+!Pdrivers/gpu/drm/drm_vma_manager.c vma offset manager
+!Edrivers/gpu/drm/drm_vma_manager.c
+!Iinclude/drm/drm_vma_manager.h
+    </sect2>
   </sect1>
 
   <!-- Internals: kms properties -->
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 801bcaf..d943b94 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -13,7 +13,7 @@ drm-y       :=	drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
 		drm_crtc.o drm_modes.o drm_edid.o \
 		drm_info.o drm_debugfs.o drm_encoder_slave.o \
 		drm_trace_points.o drm_global.o drm_prime.o \
-		drm_rect.o
+		drm_rect.o drm_vma_manager.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c
new file mode 100644
index 0000000..4b5f415
--- /dev/null
+++ b/drivers/gpu/drm/drm_vma_manager.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
+ * Copyright (c) 2012 David Airlie <airlied@linux.ie>
+ * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_mm.h>
+#include <drm/drm_vma_manager.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+/**
+ * DOC: vma offset manager
+ *
+ * The vma-manager is responsible to map arbitrary driver-dependent memory
+ * regions into the linear user address-space. It provides offsets to the
+ * caller which can then be used on the address_space of the drm-device. It
+ * takes care to not overlap regions, size them appropriately and to not
+ * confuse mm-core by inconsistent fake vm_pgoff fields.
+ * Drivers shouldn't use this for object placement in VMEM. This manager should
+ * only be used to manage mappings into linear user-space VMs.
+ *
+ * We use drm_mm as backend to manage object allocations. But it is highly
+ * optimized for alloc/free calls, not lookups. Hence, we use an rb-tree to
+ * speed up offset lookups.
+ *
+ * You must not use multiple offset managers on a single address_space.
+ * Otherwise, mm-core will be unable to tear down memory mappings as the VM will
+ * no longer be linear. Please use VM_NONLINEAR in that case and implement your
+ * own offset managers.
+ *
+ * This offset manager works on page-based addresses. That is, every argument
+ * and return code (with the exception of drm_vma_node_offset_addr()) is given
+ * in number of pages, not number of bytes. That means, object sizes and offsets
+ * must always be page-aligned (as usual).
+ * If you want to get a valid byte-based user-space address for a given offset,
+ * please see drm_vma_node_offset_addr().
+ */
+
+/**
+ * drm_vma_offset_manager_init - Initialize new offset-manager
+ * @mgr: Manager object
+ * @page_offset: Offset of available memory area (page-based)
+ * @size: Size of available memory area (page-based)
+ *
+ * Initialize a new offset-manager. The offset and area size available for the
+ * manager are given as @page_offset and @size. Both are interpreted as
+ * page-numbers, not bytes.
+ *
+ * Adding/removing nodes from the manager is locked internally and protected
+ * against concurrent access. However, node allocation and destruction is left
+ * for the caller. While calling into the vma-manager, a given node must
+ * always be guaranteed to be referenced.
+ */
+void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
+				 unsigned long page_offset, unsigned long size)
+{
+	rwlock_init(&mgr->vm_lock);
+	mgr->vm_addr_space_rb = RB_ROOT;
+	drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size);
+}
+EXPORT_SYMBOL(drm_vma_offset_manager_init);
+
+/**
+ * drm_vma_offset_manager_destroy() - Destroy offset manager
+ * @mgr: Manager object
+ *
+ * Destroy an object manager which was previously created via
+ * drm_vma_offset_manager_init(). The caller must remove all allocated nodes
+ * before destroying the manager. Otherwise, drm_mm will refuse to free the
+ * requested resources.
+ *
+ * The manager must not be accessed after this function is called.
+ */
+void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr)
+{
+	/* take the lock to protect against buggy drivers */
+	write_lock(&mgr->vm_lock);
+	drm_mm_takedown(&mgr->vm_addr_space_mm);
+	write_unlock(&mgr->vm_lock);
+}
+EXPORT_SYMBOL(drm_vma_offset_manager_destroy);
+
+/**
+ * drm_vma_offset_lookup() - Find node in offset space
+ * @mgr: Manager object
+ * @start: Start address for object (page-based)
+ * @pages: Size of object (page-based)
+ *
+ * Find a node given a start address and object size. This returns the _best_
+ * match for the given node. That is, @start may point somewhere into a valid
+ * region and the given node will be returned, as long as the node spans the
+ * whole requested area (given the size in number of pages as @pages).
+ *
+ * RETURNS:
+ * Returns NULL if no suitable node can be found. Otherwise, the best match
+ * is returned. It's the caller's responsibility to make sure the node doesn't
+ * get destroyed before the caller can access it.
+ */
+struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr,
+						  unsigned long start,
+						  unsigned long pages)
+{
+	struct drm_vma_offset_node *node, *best;
+	struct rb_node *iter;
+	unsigned long offset;
+
+	read_lock(&mgr->vm_lock);
+
+	iter = mgr->vm_addr_space_rb.rb_node;
+	best = NULL;
+
+	while (likely(iter)) {
+		node = rb_entry(iter, struct drm_vma_offset_node, vm_rb);
+		offset = node->vm_node.start;
+		if (start >= offset) {
+			iter = iter->rb_right;
+			best = node;
+			if (start == offset)
+				break;
+		} else {
+			iter = iter->rb_left;
+		}
+	}
+
+	/* verify that the node spans the requested area */
+	if (best) {
+		offset = best->vm_node.start + best->vm_pages;
+		if (offset > start + pages)
+			best = NULL;
+	}
+
+	read_unlock(&mgr->vm_lock);
+
+	return best;
+}
+EXPORT_SYMBOL(drm_vma_offset_lookup);
+
+/* internal helper to link @node into the rb-tree */
+static void _drm_vma_offset_add_rb(struct drm_vma_offset_manager *mgr,
+				   struct drm_vma_offset_node *node)
+{
+	struct rb_node **iter = &mgr->vm_addr_space_rb.rb_node;
+	struct rb_node *parent = NULL;
+	struct drm_vma_offset_node *iter_node;
+
+	while (likely(*iter)) {
+		parent = *iter;
+		iter_node = rb_entry(*iter, struct drm_vma_offset_node, vm_rb);
+
+		if (node->vm_node.start < iter_node->vm_node.start)
+			iter = &(*iter)->rb_left;
+		else if (node->vm_node.start > iter_node->vm_node.start)
+			iter = &(*iter)->rb_right;
+		else
+			BUG();
+	}
+
+	rb_link_node(&node->vm_rb, parent, iter);
+	rb_insert_color(&node->vm_rb, &mgr->vm_addr_space_rb);
+}
+
+/**
+ * drm_vma_offset_add() - Add offset node to manager
+ * @mgr: Manager object
+ * @node: Node to be added
+ * @pages: Allocation size visible to user-space (in number of pages)
+ *
+ * Add a node to the offset-manager. If the node was already added, this does
+ * nothing and return 0. @pages is the size of the object given in number of
+ * pages.
+ * After this call succeeds, you can access the offset of the node until it
+ * is removed again.
+ *
+ * If this call fails, it is safe to retry the operation or call
+ * drm_vma_offset_remove(), anyway. However, no cleanup is required in that
+ * case.
+ *
+ * @pages is not required to be the same size as the underlying memory object
+ * that you want to map. It only limits the size that user-space can map into
+ * their address space.
+ *
+ * RETURNS:
+ * 0 on success, negative error code on failure.
+ */
+int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
+		       struct drm_vma_offset_node *node, unsigned long pages)
+{
+	int ret;
+
+	write_lock(&mgr->vm_lock);
+
+	if (drm_mm_node_allocated(&node->vm_node)) {
+		ret = 0;
+		goto out_unlock;
+	}
+
+	ret = drm_mm_insert_node_generic(&mgr->vm_addr_space_mm,
+					 &node->vm_node, pages, 0, 0);
+	if (ret)
+		goto out_unlock;
+
+	node->vm_pages = pages;
+	_drm_vma_offset_add_rb(mgr, node);
+	goto out_unlock;
+
+out_unlock:
+	write_unlock(&mgr->vm_lock);
+	return ret;
+}
+EXPORT_SYMBOL(drm_vma_offset_add);
+
+/**
+ * drm_vma_offset_remove() - Remove offset node from manager
+ * @mgr: Manager object
+ * @node: Node to be removed
+ *
+ * Remove a node from the offset manager. If the node wasn't added before, this
+ * does nothing. After this call returns, the offset of the node must be not
+ * accessed, anymore.
+ * It is safe to add the node via drm_vma_offset_add() again.
+ */
+void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
+			   struct drm_vma_offset_node *node)
+{
+	write_lock(&mgr->vm_lock);
+
+	if (drm_mm_node_allocated(&node->vm_node)) {
+		rb_erase(&node->vm_rb, &mgr->vm_addr_space_rb);
+		drm_mm_remove_node(&node->vm_node);
+		memset(&node->vm_node, 0, sizeof(node->vm_node));
+		node->vm_pages = 0;
+	}
+
+	write_unlock(&mgr->vm_lock);
+}
+EXPORT_SYMBOL(drm_vma_offset_remove);
diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h
new file mode 100644
index 0000000..4fcc441
--- /dev/null
+++ b/include/drm/drm_vma_manager.h
@@ -0,0 +1,138 @@
+#ifndef __DRM_VMA_MANAGER_H__
+#define __DRM_VMA_MANAGER_H__
+
+/*
+ * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drm_mm.h>
+#include <linux/module.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+struct drm_vma_offset_node {
+	struct drm_mm_node vm_node;
+	struct rb_node vm_rb;
+	unsigned long vm_pages;
+};
+
+struct drm_vma_offset_manager {
+	rwlock_t vm_lock;
+	struct rb_root vm_addr_space_rb;
+	struct drm_mm vm_addr_space_mm;
+};
+
+void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
+				 unsigned long page_offset, unsigned long size);
+void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr);
+
+struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr,
+						  unsigned long start,
+						  unsigned long pages);
+int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
+		       struct drm_vma_offset_node *node, unsigned long pages);
+void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
+			   struct drm_vma_offset_node *node);
+
+/**
+ * drm_vma_offset_exact_lookup() - Look up node by exact address
+ * @mgr: Manager object
+ * @start: Start address (page-based, not byte-based)
+ *
+ * Same as drm_vma_offset_lookup() but does not allow any offset into the node.
+ * It only returns the exact object with the given start address.
+ *
+ * RETURNS:
+ * Node at exact start address @start.
+ */
+static inline struct drm_vma_offset_node *
+drm_vma_offset_exact_lookup(struct drm_vma_offset_manager *mgr,
+			    unsigned long start)
+{
+	return drm_vma_offset_lookup(mgr, start, 1);
+}
+
+/**
+ * drm_vma_node_reset() - Initialize or reset node object
+ * @node: Node to initialize or reset
+ *
+ * Reset a node to its initial state. You must call this before using @node with
+ * any vma offset manager.
+ *
+ * This must not be called on an already allocated node, or you will leak
+ * memory.
+ */
+static inline void drm_vma_node_reset(struct drm_vma_offset_node *node)
+{
+	memset(node, 0, sizeof(*node));
+}
+
+/**
+ * drm_vma_node_start() - Return start address for page-based addressing
+ * @node: Node to inspect
+ *
+ * Return the start address of the given node. This can be used as offset into
+ * the linear VM space that is provided by the VMA offset manager. Note that
+ * this can only be used for page-based addressing. If you need a proper offset
+ * for user-space mappings, you must apply "<< PAGE_SHIFT" or use the
+ * drm_vma_node_offset_addr() helper instead.
+ *
+ * RETURNS:
+ * Start address of @node for page-based addressing. 0 if the node does not
+ * have an offset allocated.
+ */
+static inline unsigned long drm_vma_node_start(struct drm_vma_offset_node *node)
+{
+	return node->vm_node.start;
+}
+
+/**
+ * drm_vma_node_has_offset() - Check whether node is added to offset manager
+ * @node: Node to be checked
+ *
+ * RETURNS:
+ * true iff the node was previously allocated an offset and added to
+ * an vma offset manager.
+ */
+static inline bool drm_vma_node_has_offset(struct drm_vma_offset_node *node)
+{
+	return drm_mm_node_allocated(&node->vm_node);
+}
+
+/**
+ * drm_vma_node_offset_addr() - Return sanitized offset for user-space mmaps
+ * @node: Linked offset node
+ *
+ * Same as drm_vma_node_start() but returns the address as a valid offset that
+ * can be used for user-space mappings during mmap().
+ * This must not be called on unlinked nodes.
+ *
+ * RETURNS:
+ * Offset of @node for byte-based addressing. 0 if the node does not have an
+ * object allocated.
+ */
+static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
+{
+	return ((__u64)node->vm_node.start) << PAGE_SHIFT;
+}
+
+#endif /* __DRM_VMA_MANAGER_H__ */
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v3 2/4] drm/gem: convert to new unified vma manager
  2013-07-17 18:14 [PATCH v3 0/4] Unified VMA Offset Manager v3 David Herrmann
  2013-07-17 18:14 ` [PATCH v3 1/4] drm: add unified vma offset manager David Herrmann
@ 2013-07-17 18:14 ` David Herrmann
  2013-07-18 19:01   ` Patrik Jakobsson
  2013-07-17 18:14 ` [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager David Herrmann
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 31+ messages in thread
From: David Herrmann @ 2013-07-17 18:14 UTC (permalink / raw)
  To: dri-devel; +Cc: Daniel Vetter, Dave Airlie

Use the new vma manager instead of the old hashtable. Also convert all
drivers to use the new convenience helpers. This drops all the
(map_list.hash.key << PAGE_SHIFT) non-sense.

Locking and access-management is exactly the same as before with an
additional lock inside of the vma-manager, which strictly wouldn't be
needed for gem.

v2:
 - rebase on drm-next
 - init nodes via drm_vma_node_reset() in drm_gem.c
v3:
 - fix tegra

Cc: Inki Dae <inki.dae@samsung.com>
Cc: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Rob Clark <robdclark@gmail.com>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/gpu/drm/drm_gem.c                  | 90 ++++++------------------------
 drivers/gpu/drm/drm_gem_cma_helper.c       |  9 +--
 drivers/gpu/drm/exynos/exynos_drm_gem.c    |  7 ++-
 drivers/gpu/drm/gma500/gem.c               |  8 +--
 drivers/gpu/drm/i915/i915_gem.c            |  9 +--
 drivers/gpu/drm/omapdrm/omap_gem.c         | 11 ++--
 drivers/gpu/drm/omapdrm/omap_gem_helpers.c | 49 +---------------
 drivers/gpu/drm/udl/udl_gem.c              |  6 +-
 drivers/gpu/host1x/drm/gem.c               |  4 +-
 include/drm/drmP.h                         |  7 +--
 include/uapi/drm/drm.h                     |  2 +-
 11 files changed, 51 insertions(+), 151 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 603f256..b5db89b 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -37,6 +37,7 @@
 #include <linux/shmem_fs.h>
 #include <linux/dma-buf.h>
 #include <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
 
 /** @file drm_gem.c
  *
@@ -102,14 +103,9 @@ drm_gem_init(struct drm_device *dev)
 	}
 
 	dev->mm_private = mm;
-
-	if (drm_ht_create(&mm->offset_hash, 12)) {
-		kfree(mm);
-		return -ENOMEM;
-	}
-
-	drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START,
-		    DRM_FILE_PAGE_OFFSET_SIZE);
+	drm_vma_offset_manager_init(&mm->vma_manager,
+				    DRM_FILE_PAGE_OFFSET_START,
+				    DRM_FILE_PAGE_OFFSET_SIZE);
 
 	return 0;
 }
@@ -119,8 +115,7 @@ drm_gem_destroy(struct drm_device *dev)
 {
 	struct drm_gem_mm *mm = dev->mm_private;
 
-	drm_mm_takedown(&mm->offset_manager);
-	drm_ht_remove(&mm->offset_hash);
+	drm_vma_offset_manager_destroy(&mm->vma_manager);
 	kfree(mm);
 	dev->mm_private = NULL;
 }
@@ -141,6 +136,7 @@ int drm_gem_object_init(struct drm_device *dev,
 
 	kref_init(&obj->refcount);
 	atomic_set(&obj->handle_count, 0);
+	drm_vma_node_reset(&obj->vma_node);
 	obj->size = size;
 
 	return 0;
@@ -162,6 +158,7 @@ int drm_gem_private_object_init(struct drm_device *dev,
 
 	kref_init(&obj->refcount);
 	atomic_set(&obj->handle_count, 0);
+	drm_vma_node_reset(&obj->vma_node);
 	obj->size = size;
 
 	return 0;
@@ -306,12 +303,8 @@ drm_gem_free_mmap_offset(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
 	struct drm_gem_mm *mm = dev->mm_private;
-	struct drm_map_list *list = &obj->map_list;
 
-	drm_ht_remove_item(&mm->offset_hash, &list->hash);
-	drm_mm_put_block(list->file_offset_node);
-	kfree(list->map);
-	list->map = NULL;
+	drm_vma_offset_remove(&mm->vma_manager, &obj->vma_node);
 }
 EXPORT_SYMBOL(drm_gem_free_mmap_offset);
 
@@ -331,54 +324,9 @@ drm_gem_create_mmap_offset(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
 	struct drm_gem_mm *mm = dev->mm_private;
-	struct drm_map_list *list;
-	struct drm_local_map *map;
-	int ret;
-
-	/* Set the object up for mmap'ing */
-	list = &obj->map_list;
-	list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
-	if (!list->map)
-		return -ENOMEM;
-
-	map = list->map;
-	map->type = _DRM_GEM;
-	map->size = obj->size;
-	map->handle = obj;
-
-	/* Get a DRM GEM mmap offset allocated... */
-	list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
-			obj->size / PAGE_SIZE, 0, false);
-
-	if (!list->file_offset_node) {
-		DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
-		ret = -ENOSPC;
-		goto out_free_list;
-	}
 
-	list->file_offset_node = drm_mm_get_block(list->file_offset_node,
-			obj->size / PAGE_SIZE, 0);
-	if (!list->file_offset_node) {
-		ret = -ENOMEM;
-		goto out_free_list;
-	}
-
-	list->hash.key = list->file_offset_node->start;
-	ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
-	if (ret) {
-		DRM_ERROR("failed to add to map hash\n");
-		goto out_free_mm;
-	}
-
-	return 0;
-
-out_free_mm:
-	drm_mm_put_block(list->file_offset_node);
-out_free_list:
-	kfree(list->map);
-	list->map = NULL;
-
-	return ret;
+	return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node,
+				  obj->size / PAGE_SIZE);
 }
 EXPORT_SYMBOL(drm_gem_create_mmap_offset);
 
@@ -707,8 +655,8 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
 	struct drm_file *priv = filp->private_data;
 	struct drm_device *dev = priv->minor->dev;
 	struct drm_gem_mm *mm = dev->mm_private;
-	struct drm_local_map *map = NULL;
-	struct drm_hash_item *hash;
+	struct drm_gem_object *obj;
+	struct drm_vma_offset_node *node;
 	int ret = 0;
 
 	if (drm_device_is_unplugged(dev))
@@ -716,21 +664,15 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
 
 	mutex_lock(&dev->struct_mutex);
 
-	if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
+	node = drm_vma_offset_exact_lookup(&mm->vma_manager, vma->vm_pgoff);
+	if (!node) {
 		mutex_unlock(&dev->struct_mutex);
 		return drm_mmap(filp, vma);
 	}
 
-	map = drm_hash_entry(hash, struct drm_map_list, hash)->map;
-	if (!map ||
-	    ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) {
-		ret =  -EPERM;
-		goto out_unlock;
-	}
-
-	ret = drm_gem_mmap_obj(map->handle, map->size, vma);
+	obj = container_of(node, struct drm_gem_object, vma_node);
+	ret = drm_gem_mmap_obj(obj, node->vm_pages, vma);
 
-out_unlock:
 	mutex_unlock(&dev->struct_mutex);
 
 	return ret;
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
index ece72a8..3bf8212 100644
--- a/drivers/gpu/drm/drm_gem_cma_helper.c
+++ b/drivers/gpu/drm/drm_gem_cma_helper.c
@@ -27,10 +27,11 @@
 #include <drm/drmP.h>
 #include <drm/drm.h>
 #include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_vma_manager.h>
 
 static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
 {
-	return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
+	return (unsigned int)drm_vma_node_offset_addr(&obj->vma_node);
 }
 
 /*
@@ -172,7 +173,7 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
 {
 	struct drm_gem_cma_object *cma_obj;
 
-	if (gem_obj->map_list.map)
+	if (drm_vma_node_has_offset(&gem_obj->vma_node))
 		drm_gem_free_mmap_offset(gem_obj);
 
 	cma_obj = to_drm_gem_cma_obj(gem_obj);
@@ -305,8 +306,8 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m
 
 	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
-	if (obj->map_list.map)
-		off = (uint64_t)obj->map_list.hash.key;
+	if (drm_vma_node_has_offset(&obj->vma_node))
+		off = drm_vma_node_start(&obj->vma_node);
 
 	seq_printf(m, "%2d (%2d) %08llx %08Zx %p %d",
 			obj->name, obj->refcount.refcount.counter,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index 24c22a8..a037871 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -10,6 +10,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
 
 #include <linux/shmem_fs.h>
 #include <drm/exynos_drm.h>
@@ -152,7 +153,7 @@ out:
 	exynos_drm_fini_buf(obj->dev, buf);
 	exynos_gem_obj->buffer = NULL;
 
-	if (obj->map_list.map)
+	if (drm_vma_node_has_offset(&obj->vma_node))
 		drm_gem_free_mmap_offset(obj);
 
 	/* release file pointer to gem object. */
@@ -703,13 +704,13 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
 		goto unlock;
 	}
 
-	if (!obj->map_list.map) {
+	if (!drm_vma_node_has_offset(&obj->vma_node)) {
 		ret = drm_gem_create_mmap_offset(obj);
 		if (ret)
 			goto out;
 	}
 
-	*offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
+	*offset = drm_vma_node_offset_addr(&obj->vma_node);
 	DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
 
 out:
diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c
index eefd6cc..9fd0c9f 100644
--- a/drivers/gpu/drm/gma500/gem.c
+++ b/drivers/gpu/drm/gma500/gem.c
@@ -26,6 +26,7 @@
 #include <drm/drmP.h>
 #include <drm/drm.h>
 #include <drm/gma_drm.h>
+#include <drm/drm_vma_manager.h>
 #include "psb_drv.h"
 
 int psb_gem_init_object(struct drm_gem_object *obj)
@@ -38,7 +39,7 @@ void psb_gem_free_object(struct drm_gem_object *obj)
 	struct gtt_range *gtt = container_of(obj, struct gtt_range, gem);
 
 	/* Remove the list map if one is present */
-	if (obj->map_list.map)
+	if (drm_vma_node_has_offset(&obj->vma_node))
 		drm_gem_free_mmap_offset(obj);
 	drm_gem_object_release(obj);
 
@@ -81,13 +82,12 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev,
 	/* What validation is needed here ? */
 
 	/* Make it mmapable */
-	if (!obj->map_list.map) {
+	if (!drm_vma_node_has_offset(&obj->vma_node)) {
 		ret = drm_gem_create_mmap_offset(obj);
 		if (ret)
 			goto out;
 	}
-	/* GEM should really work out the hash offsets for us */
-	*offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
+	*offset = drm_vma_node_offset_addr(&obj->vma_node);
 out:
 	drm_gem_object_unreference(obj);
 unlock:
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 769f752..ca1ceb0 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -26,6 +26,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 #include "i915_trace.h"
@@ -1427,7 +1428,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
 
 	if (obj->base.dev->dev_mapping)
 		unmap_mapping_range(obj->base.dev->dev_mapping,
-				    (loff_t)obj->base.map_list.hash.key<<PAGE_SHIFT,
+				    (loff_t)drm_vma_node_offset_addr(&obj->base.vma_node),
 				    obj->base.size, 1);
 
 	obj->fault_mappable = false;
@@ -1485,7 +1486,7 @@ static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj)
 	struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
 	int ret;
 
-	if (obj->base.map_list.map)
+	if (drm_vma_node_has_offset(&obj->base.vma_node))
 		return 0;
 
 	dev_priv->mm.shrinker_no_lock_stealing = true;
@@ -1516,7 +1517,7 @@ out:
 
 static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj)
 {
-	if (!obj->base.map_list.map)
+	if (!drm_vma_node_has_offset(&obj->base.vma_node))
 		return;
 
 	drm_gem_free_mmap_offset(&obj->base);
@@ -1557,7 +1558,7 @@ i915_gem_mmap_gtt(struct drm_file *file,
 	if (ret)
 		goto out;
 
-	*offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT;
+	*offset = drm_vma_node_offset_addr(&obj->base.vma_node);
 
 out:
 	drm_gem_object_unreference(&obj->base);
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
index ebbdf41..0bcfdb9 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -20,6 +20,7 @@
 
 #include <linux/spinlock.h>
 #include <linux/shmem_fs.h>
+#include <drm/drm_vma_manager.h>
 
 #include "omap_drv.h"
 #include "omap_dmm_tiler.h"
@@ -311,7 +312,7 @@ static uint64_t mmap_offset(struct drm_gem_object *obj)
 
 	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
-	if (!obj->map_list.map) {
+	if (!drm_vma_node_has_offset(&obj->vma_node)) {
 		/* Make it mmapable */
 		size_t size = omap_gem_mmap_size(obj);
 		int ret = _drm_gem_create_mmap_offset_size(obj, size);
@@ -322,7 +323,7 @@ static uint64_t mmap_offset(struct drm_gem_object *obj)
 		}
 	}
 
-	return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT;
+	return drm_vma_node_offset_addr(&obj->vma_node);
 }
 
 uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
@@ -1001,8 +1002,8 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
 
 	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
-	if (obj->map_list.map)
-		off = (uint64_t)obj->map_list.hash.key;
+	if (drm_vma_node_has_offset(&obj->vma_node))
+		off = drm_vma_node_start(&obj->vma_node);
 
 	seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d",
 			omap_obj->flags, obj->name, obj->refcount.refcount.counter,
@@ -1309,7 +1310,7 @@ void omap_gem_free_object(struct drm_gem_object *obj)
 
 	list_del(&omap_obj->mm_list);
 
-	if (obj->map_list.map)
+	if (drm_vma_node_has_offset(&obj->vma_node))
 		drm_gem_free_mmap_offset(obj);
 
 	/* this means the object is still pinned.. which really should
diff --git a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
index f9eb679..dbb1575 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
@@ -118,52 +118,7 @@ _drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size)
 {
 	struct drm_device *dev = obj->dev;
 	struct drm_gem_mm *mm = dev->mm_private;
-	struct drm_map_list *list;
-	struct drm_local_map *map;
-	int ret = 0;
-
-	/* Set the object up for mmap'ing */
-	list = &obj->map_list;
-	list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
-	if (!list->map)
-		return -ENOMEM;
-
-	map = list->map;
-	map->type = _DRM_GEM;
-	map->size = size;
-	map->handle = obj;
-
-	/* Get a DRM GEM mmap offset allocated... */
-	list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
-			size / PAGE_SIZE, 0, 0);
-
-	if (!list->file_offset_node) {
-		DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
-		ret = -ENOSPC;
-		goto out_free_list;
-	}
-
-	list->file_offset_node = drm_mm_get_block(list->file_offset_node,
-			size / PAGE_SIZE, 0);
-	if (!list->file_offset_node) {
-		ret = -ENOMEM;
-		goto out_free_list;
-	}
-
-	list->hash.key = list->file_offset_node->start;
-	ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
-	if (ret) {
-		DRM_ERROR("failed to add to map hash\n");
-		goto out_free_mm;
-	}
-
-	return 0;
-
-out_free_mm:
-	drm_mm_put_block(list->file_offset_node);
-out_free_list:
-	kfree(list->map);
-	list->map = NULL;
 
-	return ret;
+	return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node,
+				  size / PAGE_SIZE);
 }
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
index ef034fa..59aee8a 100644
--- a/drivers/gpu/drm/udl/udl_gem.c
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -223,7 +223,7 @@ void udl_gem_free_object(struct drm_gem_object *gem_obj)
 	if (obj->pages)
 		udl_gem_put_pages(obj);
 
-	if (gem_obj->map_list.map)
+	if (drm_vma_node_has_offset(&gem_obj->vma_node))
 		drm_gem_free_mmap_offset(gem_obj);
 }
 
@@ -247,13 +247,13 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev,
 	ret = udl_gem_get_pages(gobj, GFP_KERNEL);
 	if (ret)
 		goto out;
-	if (!gobj->base.map_list.map) {
+	if (!drm_vma_node_has_offset(&gobj->base.vma_node)) {
 		ret = drm_gem_create_mmap_offset(obj);
 		if (ret)
 			goto out;
 	}
 
-	*offset = (u64)gobj->base.map_list.hash.key << PAGE_SHIFT;
+	*offset = drm_vma_node_offset_addr(&gobj->base.vma_node);
 
 out:
 	drm_gem_object_unreference(&gobj->base);
diff --git a/drivers/gpu/host1x/drm/gem.c b/drivers/gpu/host1x/drm/gem.c
index c5e9a9b..1a0bf33 100644
--- a/drivers/gpu/host1x/drm/gem.c
+++ b/drivers/gpu/host1x/drm/gem.c
@@ -108,7 +108,7 @@ static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo)
 
 unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo)
 {
-	return (unsigned int)bo->gem.map_list.hash.key << PAGE_SHIFT;
+	return (unsigned int)drm_vma_node_offset_addr(&bo->gem.vma_node);
 }
 
 struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
@@ -182,7 +182,7 @@ void tegra_bo_free_object(struct drm_gem_object *gem)
 {
 	struct tegra_bo *bo = to_tegra_bo(gem);
 
-	if (gem->map_list.map)
+	if (drm_vma_node_has_offset(&gem->vma_node))
 		drm_gem_free_mmap_offset(gem);
 
 	drm_gem_object_release(gem);
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 12083dc..93a5800 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -71,6 +71,7 @@
 #include <asm/pgalloc.h>
 #include <drm/drm.h>
 #include <drm/drm_sarea.h>
+#include <drm/drm_vma_manager.h>
 
 #include <linux/idr.h>
 
@@ -587,7 +588,6 @@ struct drm_map_list {
 	struct drm_local_map *map;	/**< mapping */
 	uint64_t user_token;
 	struct drm_master *master;
-	struct drm_mm_node *file_offset_node;	/**< fake offset */
 };
 
 /**
@@ -622,8 +622,7 @@ struct drm_ati_pcigart_info {
  * GEM specific mm private for tracking GEM objects
  */
 struct drm_gem_mm {
-	struct drm_mm offset_manager;	/**< Offset mgmt for buffer objects */
-	struct drm_open_hash offset_hash; /**< User token hash table for maps */
+	struct drm_vma_offset_manager vma_manager;
 };
 
 /**
@@ -644,7 +643,7 @@ struct drm_gem_object {
 	struct file *filp;
 
 	/* Mapping info for this object */
-	struct drm_map_list map_list;
+	struct drm_vma_offset_node vma_node;
 
 	/**
 	 * Size of the object, in bytes.  Immutable over the object's
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index 238a166..272580c 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -181,7 +181,7 @@ enum drm_map_type {
 	_DRM_AGP = 3,		  /**< AGP/GART */
 	_DRM_SCATTER_GATHER = 4,  /**< Scatter/gather memory for PCI DMA */
 	_DRM_CONSISTENT = 5,	  /**< Consistent memory for PCI DMA */
-	_DRM_GEM = 6,		  /**< GEM object */
+	_DRM_GEM = 6,		  /**< GEM object (obsolete) */
 };
 
 /**
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-17 18:14 [PATCH v3 0/4] Unified VMA Offset Manager v3 David Herrmann
  2013-07-17 18:14 ` [PATCH v3 1/4] drm: add unified vma offset manager David Herrmann
  2013-07-17 18:14 ` [PATCH v3 2/4] drm/gem: convert to new unified vma manager David Herrmann
@ 2013-07-17 18:14 ` David Herrmann
  2013-07-18  8:53   ` Thomas Hellstrom
  2013-07-17 18:14 ` [PATCH v3 4/4] drm/vma: provide drm_vma_node_unmap() helper David Herrmann
  2013-07-23 12:47 ` [PATCH v4 0/4] Unified VMA Offset Manager David Herrmann
  4 siblings, 1 reply; 31+ messages in thread
From: David Herrmann @ 2013-07-17 18:14 UTC (permalink / raw)
  To: dri-devel
  Cc: Thomas Hellstrom, Martin Peres, Alex Deucher, Ben Skeggs, Dave Airlie

Use the new vma-manager infrastructure. This doesn't change any
implementation details as the vma-offset-manager is nearly copied 1-to-1
from TTM.

Even though the vma-manager uses its own locks, we still need bo->vm_lock
to prevent bos from being destroyed before we can get a reference during
lookup. However, this lock is not needed during vm-setup as we still
hold a reference there.

This also drops the addr_space_offset member as it is a copy of vm_start
in vma_node objects. Use the accessor functions instead.

Cc: Dave Airlie <airlied@redhat.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Cc: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Cc: Martin Peres <martin.peres@labri.fr>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/gpu/drm/ast/ast_main.c            |  2 +-
 drivers/gpu/drm/cirrus/cirrus_main.c      |  2 +-
 drivers/gpu/drm/mgag200/mgag200_main.c    |  2 +-
 drivers/gpu/drm/nouveau/nouveau_display.c |  2 +-
 drivers/gpu/drm/nouveau/nouveau_gem.c     |  2 +-
 drivers/gpu/drm/qxl/qxl_object.h          |  2 +-
 drivers/gpu/drm/qxl/qxl_release.c         |  2 +-
 drivers/gpu/drm/radeon/radeon_object.h    |  5 +-
 drivers/gpu/drm/ttm/ttm_bo.c              | 84 ++++++-------------------------
 drivers/gpu/drm/ttm/ttm_bo_util.c         |  3 +-
 drivers/gpu/drm/ttm/ttm_bo_vm.c           | 81 ++++++++++++-----------------
 drivers/gpu/drm/vmwgfx/vmwgfx_resource.c  |  4 +-
 include/drm/ttm/ttm_bo_api.h              | 15 ++----
 include/drm/ttm/ttm_bo_driver.h           |  7 +--
 14 files changed, 65 insertions(+), 148 deletions(-)

diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
index f60fd7b..c195dc2 100644
--- a/drivers/gpu/drm/ast/ast_main.c
+++ b/drivers/gpu/drm/ast/ast_main.c
@@ -487,7 +487,7 @@ void ast_gem_free_object(struct drm_gem_object *obj)
 
 static inline u64 ast_bo_mmap_offset(struct ast_bo *bo)
 {
-	return bo->bo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->bo.vma_node);
 }
 int
 ast_dumb_mmap_offset(struct drm_file *file,
diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c
index 35cbae8..3a7a0ef 100644
--- a/drivers/gpu/drm/cirrus/cirrus_main.c
+++ b/drivers/gpu/drm/cirrus/cirrus_main.c
@@ -294,7 +294,7 @@ void cirrus_gem_free_object(struct drm_gem_object *obj)
 
 static inline u64 cirrus_bo_mmap_offset(struct cirrus_bo *bo)
 {
-	return bo->bo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->bo.vma_node);
 }
 
 int
diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c
index 9fa5685..1a75ea3 100644
--- a/drivers/gpu/drm/mgag200/mgag200_main.c
+++ b/drivers/gpu/drm/mgag200/mgag200_main.c
@@ -349,7 +349,7 @@ void mgag200_gem_free_object(struct drm_gem_object *obj)
 
 static inline u64 mgag200_bo_mmap_offset(struct mgag200_bo *bo)
 {
-	return bo->bo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->bo.vma_node);
 }
 
 int
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 708b2d1..7a8caa1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -697,7 +697,7 @@ nouveau_display_dumb_map_offset(struct drm_file *file_priv,
 	gem = drm_gem_object_lookup(dev, file_priv, handle);
 	if (gem) {
 		struct nouveau_bo *bo = gem->driver_private;
-		*poffset = bo->bo.addr_space_offset;
+		*poffset = drm_vma_node_offset_addr(&bo->bo.vma_node);
 		drm_gem_object_unreference_unlocked(gem);
 		return 0;
 	}
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index e72d09c..86597eb 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -226,7 +226,7 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem,
 	}
 
 	rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
-	rep->map_handle = nvbo->bo.addr_space_offset;
+	rep->map_handle = drm_vma_node_offset_addr(&nvbo->bo.vma_node);
 	rep->tile_mode = nvbo->tile_mode;
 	rep->tile_flags = nvbo->tile_flags;
 	return 0;
diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h
index ee7ad79..af10165 100644
--- a/drivers/gpu/drm/qxl/qxl_object.h
+++ b/drivers/gpu/drm/qxl/qxl_object.h
@@ -59,7 +59,7 @@ static inline unsigned long qxl_bo_size(struct qxl_bo *bo)
 
 static inline u64 qxl_bo_mmap_offset(struct qxl_bo *bo)
 {
-	return bo->tbo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->tbo.vma_node);
 }
 
 static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type,
diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c
index b443d67..1a648e1 100644
--- a/drivers/gpu/drm/qxl/qxl_release.c
+++ b/drivers/gpu/drm/qxl/qxl_release.c
@@ -87,7 +87,7 @@ qxl_release_free(struct qxl_device *qdev,
 
 	for (i = 0 ; i < release->bo_count; ++i) {
 		QXL_INFO(qdev, "release %llx\n",
-			release->bos[i]->tbo.addr_space_offset
+			drm_vma_node_offset_addr(&release->bos[i]->tbo.vma_node)
 						- DRM_FILE_OFFSET);
 		qxl_fence_remove_release(&release->bos[i]->fence, release->id);
 		qxl_bo_unref(&release->bos[i]);
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index 91519a5..188c682 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -113,13 +113,10 @@ static inline unsigned radeon_bo_gpu_page_alignment(struct radeon_bo *bo)
  * @bo:	radeon object for which we query the offset
  *
  * Returns mmap offset of the object.
- *
- * Note: addr_space_offset is constant after ttm bo init thus isn't protected
- * by any lock.
  */
 static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo)
 {
-	return bo->tbo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->tbo.vma_node);
 }
 
 extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index cb9dd67..245850b 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -616,11 +616,7 @@ static void ttm_bo_release(struct kref *kref)
 	struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type];
 
 	write_lock(&bdev->vm_lock);
-	if (likely(bo->vm_node != NULL)) {
-		rb_erase(&bo->vm_rb, &bdev->addr_space_rb);
-		drm_mm_put_block(bo->vm_node);
-		bo->vm_node = NULL;
-	}
+	drm_vma_offset_remove(&bdev->vma_manager, &bo->vma_node);
 	write_unlock(&bdev->vm_lock);
 	ttm_mem_io_lock(man, false);
 	ttm_mem_io_free_vm(bo);
@@ -1129,6 +1125,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
 	bo->resv = &bo->ttm_resv;
 	reservation_object_init(bo->resv);
 	atomic_inc(&bo->glob->bo_count);
+	drm_vma_node_reset(&bo->vma_node);
 
 	ret = ttm_bo_check_placement(bo, placement);
 
@@ -1424,9 +1421,8 @@ int ttm_bo_device_release(struct ttm_bo_device *bdev)
 		TTM_DEBUG("Swap list was clean\n");
 	spin_unlock(&glob->lru_lock);
 
-	BUG_ON(!drm_mm_clean(&bdev->addr_space_mm));
 	write_lock(&bdev->vm_lock);
-	drm_mm_takedown(&bdev->addr_space_mm);
+	drm_vma_offset_manager_destroy(&bdev->vma_manager);
 	write_unlock(&bdev->vm_lock);
 
 	return ret;
@@ -1454,9 +1450,8 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
 	if (unlikely(ret != 0))
 		goto out_no_sys;
 
-	bdev->addr_space_rb = RB_ROOT;
-	drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000);
-
+	drm_vma_offset_manager_init(&bdev->vma_manager, file_page_offset,
+				    0x10000000);
 	INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue);
 	INIT_LIST_HEAD(&bdev->ddestroy);
 	bdev->dev_mapping = NULL;
@@ -1498,12 +1493,17 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
 void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo)
 {
 	struct ttm_bo_device *bdev = bo->bdev;
-	loff_t offset = (loff_t) bo->addr_space_offset;
-	loff_t holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
+	loff_t offset, holelen;
 
 	if (!bdev->dev_mapping)
 		return;
-	unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
+
+	if (drm_vma_node_has_offset(&bo->vma_node)) {
+		offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node);
+		holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
+
+		unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
+	}
 	ttm_mem_io_free_vm(bo);
 }
 
@@ -1520,31 +1520,6 @@ void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo)
 
 EXPORT_SYMBOL(ttm_bo_unmap_virtual);
 
-static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo)
-{
-	struct ttm_bo_device *bdev = bo->bdev;
-	struct rb_node **cur = &bdev->addr_space_rb.rb_node;
-	struct rb_node *parent = NULL;
-	struct ttm_buffer_object *cur_bo;
-	unsigned long offset = bo->vm_node->start;
-	unsigned long cur_offset;
-
-	while (*cur) {
-		parent = *cur;
-		cur_bo = rb_entry(parent, struct ttm_buffer_object, vm_rb);
-		cur_offset = cur_bo->vm_node->start;
-		if (offset < cur_offset)
-			cur = &parent->rb_left;
-		else if (offset > cur_offset)
-			cur = &parent->rb_right;
-		else
-			BUG();
-	}
-
-	rb_link_node(&bo->vm_rb, parent, cur);
-	rb_insert_color(&bo->vm_rb, &bdev->addr_space_rb);
-}
-
 /**
  * ttm_bo_setup_vm:
  *
@@ -1559,38 +1534,9 @@ static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo)
 static int ttm_bo_setup_vm(struct ttm_buffer_object *bo)
 {
 	struct ttm_bo_device *bdev = bo->bdev;
-	int ret;
-
-retry_pre_get:
-	ret = drm_mm_pre_get(&bdev->addr_space_mm);
-	if (unlikely(ret != 0))
-		return ret;
 
-	write_lock(&bdev->vm_lock);
-	bo->vm_node = drm_mm_search_free(&bdev->addr_space_mm,
-					 bo->mem.num_pages, 0, 0);
-
-	if (unlikely(bo->vm_node == NULL)) {
-		ret = -ENOMEM;
-		goto out_unlock;
-	}
-
-	bo->vm_node = drm_mm_get_block_atomic(bo->vm_node,
-					      bo->mem.num_pages, 0);
-
-	if (unlikely(bo->vm_node == NULL)) {
-		write_unlock(&bdev->vm_lock);
-		goto retry_pre_get;
-	}
-
-	ttm_bo_vm_insert_rb(bo);
-	write_unlock(&bdev->vm_lock);
-	bo->addr_space_offset = ((uint64_t) bo->vm_node->start) << PAGE_SHIFT;
-
-	return 0;
-out_unlock:
-	write_unlock(&bdev->vm_lock);
-	return ret;
+	return drm_vma_offset_add(&bdev->vma_manager, &bo->vma_node,
+				  bo->mem.num_pages);
 }
 
 int ttm_bo_wait(struct ttm_buffer_object *bo,
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 319cf41..7cc904d 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -30,6 +30,7 @@
 
 #include <drm/ttm/ttm_bo_driver.h>
 #include <drm/ttm/ttm_placement.h>
+#include <drm/drm_vma_manager.h>
 #include <linux/io.h>
 #include <linux/highmem.h>
 #include <linux/wait.h>
@@ -450,7 +451,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
 	INIT_LIST_HEAD(&fbo->lru);
 	INIT_LIST_HEAD(&fbo->swap);
 	INIT_LIST_HEAD(&fbo->io_reserve_lru);
-	fbo->vm_node = NULL;
+	drm_vma_node_reset(&fbo->vma_node);
 	atomic_set(&fbo->cpu_writers, 0);
 
 	spin_lock(&bdev->fence_lock);
diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
index 3df9f16..54a67f1 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
@@ -33,6 +33,7 @@
 #include <ttm/ttm_module.h>
 #include <ttm/ttm_bo_driver.h>
 #include <ttm/ttm_placement.h>
+#include <drm/drm_vma_manager.h>
 #include <linux/mm.h>
 #include <linux/rbtree.h>
 #include <linux/module.h>
@@ -40,37 +41,6 @@
 
 #define TTM_BO_VM_NUM_PREFAULT 16
 
-static struct ttm_buffer_object *ttm_bo_vm_lookup_rb(struct ttm_bo_device *bdev,
-						     unsigned long page_start,
-						     unsigned long num_pages)
-{
-	struct rb_node *cur = bdev->addr_space_rb.rb_node;
-	unsigned long cur_offset;
-	struct ttm_buffer_object *bo;
-	struct ttm_buffer_object *best_bo = NULL;
-
-	while (likely(cur != NULL)) {
-		bo = rb_entry(cur, struct ttm_buffer_object, vm_rb);
-		cur_offset = bo->vm_node->start;
-		if (page_start >= cur_offset) {
-			cur = cur->rb_right;
-			best_bo = bo;
-			if (page_start == cur_offset)
-				break;
-		} else
-			cur = cur->rb_left;
-	}
-
-	if (unlikely(best_bo == NULL))
-		return NULL;
-
-	if (unlikely((best_bo->vm_node->start + best_bo->num_pages) <
-		     (page_start + num_pages)))
-		return NULL;
-
-	return best_bo;
-}
-
 static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
 	struct ttm_buffer_object *bo = (struct ttm_buffer_object *)
@@ -146,9 +116,9 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 	}
 
 	page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) +
-	    bo->vm_node->start - vma->vm_pgoff;
+	    drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
 	page_last = vma_pages(vma) +
-	    bo->vm_node->start - vma->vm_pgoff;
+	    drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
 
 	if (unlikely(page_offset >= bo->num_pages)) {
 		retval = VM_FAULT_SIGBUS;
@@ -249,6 +219,30 @@ static const struct vm_operations_struct ttm_bo_vm_ops = {
 	.close = ttm_bo_vm_close
 };
 
+static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device *bdev,
+						  unsigned long offset,
+						  unsigned long pages)
+{
+	struct drm_vma_offset_node *node;
+	struct ttm_buffer_object *bo = NULL;
+
+	read_lock(&bdev->vm_lock);
+
+	node = drm_vma_offset_lookup(&bdev->vma_manager, offset, pages);
+	if (likely(node)) {
+		bo = container_of(node, struct ttm_buffer_object, vma_node);
+		if (!kref_get_unless_zero(&bo->kref))
+			bo = NULL;
+	}
+
+	read_unlock(&bdev->vm_lock);
+
+	if (!bo)
+		pr_err("Could not find buffer object to map\n");
+
+	return bo;
+}
+
 int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
 		struct ttm_bo_device *bdev)
 {
@@ -256,17 +250,9 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
 	struct ttm_buffer_object *bo;
 	int ret;
 
-	read_lock(&bdev->vm_lock);
-	bo = ttm_bo_vm_lookup_rb(bdev, vma->vm_pgoff,
-				 vma_pages(vma));
-	if (likely(bo != NULL) && !kref_get_unless_zero(&bo->kref))
-		bo = NULL;
-	read_unlock(&bdev->vm_lock);
-
-	if (unlikely(bo == NULL)) {
-		pr_err("Could not find buffer object to map\n");
+	bo = ttm_bo_vm_lookup(bdev, vma->vm_pgoff, vma_pages(vma));
+	if (unlikely(!bo))
 		return -EINVAL;
-	}
 
 	driver = bo->bdev->driver;
 	if (unlikely(!driver->verify_access)) {
@@ -324,12 +310,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
 	bool no_wait = false;
 	bool dummy;
 
-	read_lock(&bdev->vm_lock);
-	bo = ttm_bo_vm_lookup_rb(bdev, dev_offset, 1);
-	if (likely(bo != NULL))
-		ttm_bo_reference(bo);
-	read_unlock(&bdev->vm_lock);
-
+	bo = ttm_bo_vm_lookup(bdev, dev_offset, 1);
 	if (unlikely(bo == NULL))
 		return -EFAULT;
 
@@ -343,7 +324,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
 	if (unlikely(ret != 0))
 		goto out_unref;
 
-	kmap_offset = dev_offset - bo->vm_node->start;
+	kmap_offset = dev_offset - drm_vma_node_start(&bo->vma_node);
 	if (unlikely(kmap_offset >= bo->num_pages)) {
 		ret = -EFBIG;
 		goto out_unref;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 7953d1f..0e67cf4 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -500,7 +500,7 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
 		goto out_no_dmabuf;
 
 	rep->handle = handle;
-	rep->map_handle = dma_buf->base.addr_space_offset;
+	rep->map_handle = drm_vma_node_offset_addr(&dma_buf->base.vma_node);
 	rep->cur_gmr_id = handle;
 	rep->cur_gmr_offset = 0;
 
@@ -834,7 +834,7 @@ int vmw_dumb_map_offset(struct drm_file *file_priv,
 	if (ret != 0)
 		return -EINVAL;
 
-	*offset = out_buf->base.addr_space_offset;
+	*offset = drm_vma_node_offset_addr(&out_buf->base.vma_node);
 	vmw_dmabuf_unreference(&out_buf);
 	return 0;
 }
diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
index 8a6aa56..751eaff 100644
--- a/include/drm/ttm/ttm_bo_api.h
+++ b/include/drm/ttm/ttm_bo_api.h
@@ -32,12 +32,12 @@
 #define _TTM_BO_API_H_
 
 #include <drm/drm_hashtab.h>
+#include <drm/drm_vma_manager.h>
 #include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/wait.h>
 #include <linux/mutex.h>
 #include <linux/mm.h>
-#include <linux/rbtree.h>
 #include <linux/bitmap.h>
 #include <linux/reservation.h>
 
@@ -145,7 +145,6 @@ struct ttm_tt;
  * @type: The bo type.
  * @destroy: Destruction function. If NULL, kfree is used.
  * @num_pages: Actual number of pages.
- * @addr_space_offset: Address space offset.
  * @acc_size: Accounted size for this object.
  * @kref: Reference count of this buffer object. When this refcount reaches
  * zero, the object is put on the delayed delete list.
@@ -166,8 +165,7 @@ struct ttm_tt;
  * @swap: List head for swap LRU list.
  * @sync_obj: Pointer to a synchronization object.
  * @priv_flags: Flags describing buffer object internal state.
- * @vm_rb: Rb node for the vm rb tree.
- * @vm_node: Address space manager node.
+ * @vma_node: Address space manager node.
  * @offset: The current GPU offset, which can have different meanings
  * depending on the memory type. For SYSTEM type memory, it should be 0.
  * @cur_placement: Hint of current placement.
@@ -194,7 +192,6 @@ struct ttm_buffer_object {
 	enum ttm_bo_type type;
 	void (*destroy) (struct ttm_buffer_object *);
 	unsigned long num_pages;
-	uint64_t addr_space_offset;
 	size_t acc_size;
 
 	/**
@@ -238,13 +235,7 @@ struct ttm_buffer_object {
 	void *sync_obj;
 	unsigned long priv_flags;
 
-	/**
-	 * Members protected by the bdev::vm_lock
-	 */
-
-	struct rb_node vm_rb;
-	struct drm_mm_node *vm_node;
-
+	struct drm_vma_offset_node vma_node;
 
 	/**
 	 * Special members that are protected by the reserve lock
diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
index 984fc2d..d3b8479 100644
--- a/include/drm/ttm/ttm_bo_driver.h
+++ b/include/drm/ttm/ttm_bo_driver.h
@@ -36,6 +36,7 @@
 #include <ttm/ttm_placement.h>
 #include <drm/drm_mm.h>
 #include <drm/drm_global.h>
+#include <drm/drm_vma_manager.h>
 #include <linux/workqueue.h>
 #include <linux/fs.h>
 #include <linux/spinlock.h>
@@ -519,7 +520,7 @@ struct ttm_bo_global {
  * @man: An array of mem_type_managers.
  * @fence_lock: Protects the synchronizing members on *all* bos belonging
  * to this device.
- * @addr_space_mm: Range manager for the device address space.
+ * @vma_manager: Address space manager
  * lru_lock: Spinlock that protects the buffer+device lru lists and
  * ddestroy lists.
  * @val_seq: Current validation sequence.
@@ -540,11 +541,11 @@ struct ttm_bo_device {
 	rwlock_t vm_lock;
 	struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES];
 	spinlock_t fence_lock;
+
 	/*
 	 * Protected by the vm lock.
 	 */
-	struct rb_root addr_space_rb;
-	struct drm_mm addr_space_mm;
+	struct drm_vma_offset_manager vma_manager;
 
 	/*
 	 * Protected by the global:lru lock.
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v3 4/4] drm/vma: provide drm_vma_node_unmap() helper
  2013-07-17 18:14 [PATCH v3 0/4] Unified VMA Offset Manager v3 David Herrmann
                   ` (2 preceding siblings ...)
  2013-07-17 18:14 ` [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager David Herrmann
@ 2013-07-17 18:14 ` David Herrmann
  2013-07-23 12:47 ` [PATCH v4 0/4] Unified VMA Offset Manager David Herrmann
  4 siblings, 0 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-17 18:14 UTC (permalink / raw)
  To: dri-devel; +Cc: Dave Airlie, Thomas Hellstrom

Instead of unmapping the nodes in TTM and GEM users manually, we provide
a generic wrapper which does the correct thing for all vma-nodes.

v2: remove bdev->dev_mapping test in ttm_bo_unmap_virtual_unlocked() as
ttm_mem_io_free_vm() does nothing in that case (io_reserved_vm is 0).

Cc: Dave Airlie <airlied@redhat.com>
Cc: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Cc: Thomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/gpu/drm/i915/i915_gem.c |  6 +-----
 drivers/gpu/drm/ttm/ttm_bo.c    | 11 +----------
 include/drm/drm_vma_manager.h   | 16 ++++++++++++++++
 3 files changed, 18 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index ca1ceb0..8ab41a5 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1426,11 +1426,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
 	if (!obj->fault_mappable)
 		return;
 
-	if (obj->base.dev->dev_mapping)
-		unmap_mapping_range(obj->base.dev->dev_mapping,
-				    (loff_t)drm_vma_node_offset_addr(&obj->base.vma_node),
-				    obj->base.size, 1);
-
+	drm_vma_node_unmap(&obj->base.vma_node, obj->base.dev->dev_mapping);
 	obj->fault_mappable = false;
 }
 
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 245850b..3fec6f99 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1493,17 +1493,8 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
 void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo)
 {
 	struct ttm_bo_device *bdev = bo->bdev;
-	loff_t offset, holelen;
 
-	if (!bdev->dev_mapping)
-		return;
-
-	if (drm_vma_node_has_offset(&bo->vma_node)) {
-		offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node);
-		holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
-
-		unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
-	}
+	drm_vma_node_unmap(&bo->vma_node, bdev->dev_mapping);
 	ttm_mem_io_free_vm(bo);
 }
 
diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h
index 4fcc441..355a0e5 100644
--- a/include/drm/drm_vma_manager.h
+++ b/include/drm/drm_vma_manager.h
@@ -24,6 +24,7 @@
  */
 
 #include <drm/drm_mm.h>
+#include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/rbtree.h>
 #include <linux/spinlock.h>
@@ -135,4 +136,19 @@ static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
 	return ((__u64)node->vm_node.start) << PAGE_SHIFT;
 }
 
+/** drm_vma_node_unmap()
+ *
+ * Unmap all userspace mappings for a given offset node. The mappings must be
+ * associated with the @file_mapping address-space. If no offset exists or
+ * the address-space is invalid, nothing is done.
+ */
+static inline void drm_vma_node_unmap(struct drm_vma_offset_node *node,
+				      struct address_space *file_mapping)
+{
+	if (file_mapping && drm_vma_node_has_offset(node))
+		unmap_mapping_range(file_mapping,
+				    drm_vma_node_offset_addr(node),
+				    node->vm_pages << PAGE_SHIFT, 1);
+}
+
 #endif /* __DRM_VMA_MANAGER_H__ */
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-17 18:14 ` [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager David Herrmann
@ 2013-07-18  8:53   ` Thomas Hellstrom
  2013-07-18 11:02     ` Daniel Vetter
  2013-07-18 11:07     ` David Herrmann
  0 siblings, 2 replies; 31+ messages in thread
From: Thomas Hellstrom @ 2013-07-18  8:53 UTC (permalink / raw)
  To: David Herrmann
  Cc: Martin Peres, dri-devel, Alex Deucher, Ben Skeggs, Dave Airlie

A quick look, but not a full review:

Looks mostly good, but it looks like the TTM vm lock isn't needed at all 
anymore (provided the vma offset manager is properly protected), since
kref_get_unless_zero() is used when a reference after lookup is taken. 
(please see the kref_get_unless_zero documentation examples). When 
drm_vma_offset_remove() is called, the kref value is unconditionally 
zero, and that should block any successful lookup.

Otherwise, if the vma offset manager always needs external locking to 
make lookup + referencing atomic, then perhaps locking should be completely
left to the caller?

Thanks,
Thomas



On 07/17/2013 08:14 PM, David Herrmann wrote:
> Use the new vma-manager infrastructure. This doesn't change any
> implementation details as the vma-offset-manager is nearly copied 1-to-1
> from TTM.
>
> Even though the vma-manager uses its own locks, we still need bo->vm_lock
> to prevent bos from being destroyed before we can get a reference during
> lookup. However, this lock is not needed during vm-setup as we still
> hold a reference there.
>
> This also drops the addr_space_offset member as it is a copy of vm_start
> in vma_node objects. Use the accessor functions instead.
>
> Cc: Dave Airlie <airlied@redhat.com>
> Cc: Ben Skeggs <bskeggs@redhat.com>
> Cc: Maarten Lankhorst <maarten.lankhorst@canonical.com>
> Cc: Martin Peres <martin.peres@labri.fr>
> Cc: Alex Deucher <alexander.deucher@amd.com>
> Cc: Thomas Hellstrom <thellstrom@vmware.com>
> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
> ---
>   drivers/gpu/drm/ast/ast_main.c            |  2 +-
>   drivers/gpu/drm/cirrus/cirrus_main.c      |  2 +-
>   drivers/gpu/drm/mgag200/mgag200_main.c    |  2 +-
>   drivers/gpu/drm/nouveau/nouveau_display.c |  2 +-
>   drivers/gpu/drm/nouveau/nouveau_gem.c     |  2 +-
>   drivers/gpu/drm/qxl/qxl_object.h          |  2 +-
>   drivers/gpu/drm/qxl/qxl_release.c         |  2 +-
>   drivers/gpu/drm/radeon/radeon_object.h    |  5 +-
>   drivers/gpu/drm/ttm/ttm_bo.c              | 84 ++++++-------------------------
>   drivers/gpu/drm/ttm/ttm_bo_util.c         |  3 +-
>   drivers/gpu/drm/ttm/ttm_bo_vm.c           | 81 ++++++++++++-----------------
>   drivers/gpu/drm/vmwgfx/vmwgfx_resource.c  |  4 +-
>   include/drm/ttm/ttm_bo_api.h              | 15 ++----
>   include/drm/ttm/ttm_bo_driver.h           |  7 +--
>   14 files changed, 65 insertions(+), 148 deletions(-)
>
> diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
> index f60fd7b..c195dc2 100644
> --- a/drivers/gpu/drm/ast/ast_main.c
> +++ b/drivers/gpu/drm/ast/ast_main.c
> @@ -487,7 +487,7 @@ void ast_gem_free_object(struct drm_gem_object *obj)
>   
>   static inline u64 ast_bo_mmap_offset(struct ast_bo *bo)
>   {
> -	return bo->bo.addr_space_offset;
> +	return drm_vma_node_offset_addr(&bo->bo.vma_node);
>   }
>   int
>   ast_dumb_mmap_offset(struct drm_file *file,
> diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c
> index 35cbae8..3a7a0ef 100644
> --- a/drivers/gpu/drm/cirrus/cirrus_main.c
> +++ b/drivers/gpu/drm/cirrus/cirrus_main.c
> @@ -294,7 +294,7 @@ void cirrus_gem_free_object(struct drm_gem_object *obj)
>   
>   static inline u64 cirrus_bo_mmap_offset(struct cirrus_bo *bo)
>   {
> -	return bo->bo.addr_space_offset;
> +	return drm_vma_node_offset_addr(&bo->bo.vma_node);
>   }
>   
>   int
> diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c
> index 9fa5685..1a75ea3 100644
> --- a/drivers/gpu/drm/mgag200/mgag200_main.c
> +++ b/drivers/gpu/drm/mgag200/mgag200_main.c
> @@ -349,7 +349,7 @@ void mgag200_gem_free_object(struct drm_gem_object *obj)
>   
>   static inline u64 mgag200_bo_mmap_offset(struct mgag200_bo *bo)
>   {
> -	return bo->bo.addr_space_offset;
> +	return drm_vma_node_offset_addr(&bo->bo.vma_node);
>   }
>   
>   int
> diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
> index 708b2d1..7a8caa1 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_display.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_display.c
> @@ -697,7 +697,7 @@ nouveau_display_dumb_map_offset(struct drm_file *file_priv,
>   	gem = drm_gem_object_lookup(dev, file_priv, handle);
>   	if (gem) {
>   		struct nouveau_bo *bo = gem->driver_private;
> -		*poffset = bo->bo.addr_space_offset;
> +		*poffset = drm_vma_node_offset_addr(&bo->bo.vma_node);
>   		drm_gem_object_unreference_unlocked(gem);
>   		return 0;
>   	}
> diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
> index e72d09c..86597eb 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_gem.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
> @@ -226,7 +226,7 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem,
>   	}
>   
>   	rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
> -	rep->map_handle = nvbo->bo.addr_space_offset;
> +	rep->map_handle = drm_vma_node_offset_addr(&nvbo->bo.vma_node);
>   	rep->tile_mode = nvbo->tile_mode;
>   	rep->tile_flags = nvbo->tile_flags;
>   	return 0;
> diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h
> index ee7ad79..af10165 100644
> --- a/drivers/gpu/drm/qxl/qxl_object.h
> +++ b/drivers/gpu/drm/qxl/qxl_object.h
> @@ -59,7 +59,7 @@ static inline unsigned long qxl_bo_size(struct qxl_bo *bo)
>   
>   static inline u64 qxl_bo_mmap_offset(struct qxl_bo *bo)
>   {
> -	return bo->tbo.addr_space_offset;
> +	return drm_vma_node_offset_addr(&bo->tbo.vma_node);
>   }
>   
>   static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type,
> diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c
> index b443d67..1a648e1 100644
> --- a/drivers/gpu/drm/qxl/qxl_release.c
> +++ b/drivers/gpu/drm/qxl/qxl_release.c
> @@ -87,7 +87,7 @@ qxl_release_free(struct qxl_device *qdev,
>   
>   	for (i = 0 ; i < release->bo_count; ++i) {
>   		QXL_INFO(qdev, "release %llx\n",
> -			release->bos[i]->tbo.addr_space_offset
> +			drm_vma_node_offset_addr(&release->bos[i]->tbo.vma_node)
>   						- DRM_FILE_OFFSET);
>   		qxl_fence_remove_release(&release->bos[i]->fence, release->id);
>   		qxl_bo_unref(&release->bos[i]);
> diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
> index 91519a5..188c682 100644
> --- a/drivers/gpu/drm/radeon/radeon_object.h
> +++ b/drivers/gpu/drm/radeon/radeon_object.h
> @@ -113,13 +113,10 @@ static inline unsigned radeon_bo_gpu_page_alignment(struct radeon_bo *bo)
>    * @bo:	radeon object for which we query the offset
>    *
>    * Returns mmap offset of the object.
> - *
> - * Note: addr_space_offset is constant after ttm bo init thus isn't protected
> - * by any lock.
>    */
>   static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo)
>   {
> -	return bo->tbo.addr_space_offset;
> +	return drm_vma_node_offset_addr(&bo->tbo.vma_node);
>   }
>   
>   extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
> index cb9dd67..245850b 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> @@ -616,11 +616,7 @@ static void ttm_bo_release(struct kref *kref)
>   	struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type];
>   
>   	write_lock(&bdev->vm_lock);
> -	if (likely(bo->vm_node != NULL)) {
> -		rb_erase(&bo->vm_rb, &bdev->addr_space_rb);
> -		drm_mm_put_block(bo->vm_node);
> -		bo->vm_node = NULL;
> -	}
> +	drm_vma_offset_remove(&bdev->vma_manager, &bo->vma_node);
>   	write_unlock(&bdev->vm_lock);
>   	ttm_mem_io_lock(man, false);
>   	ttm_mem_io_free_vm(bo);
> @@ -1129,6 +1125,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
>   	bo->resv = &bo->ttm_resv;
>   	reservation_object_init(bo->resv);
>   	atomic_inc(&bo->glob->bo_count);
> +	drm_vma_node_reset(&bo->vma_node);
>   
>   	ret = ttm_bo_check_placement(bo, placement);
>   
> @@ -1424,9 +1421,8 @@ int ttm_bo_device_release(struct ttm_bo_device *bdev)
>   		TTM_DEBUG("Swap list was clean\n");
>   	spin_unlock(&glob->lru_lock);
>   
> -	BUG_ON(!drm_mm_clean(&bdev->addr_space_mm));
>   	write_lock(&bdev->vm_lock);
> -	drm_mm_takedown(&bdev->addr_space_mm);
> +	drm_vma_offset_manager_destroy(&bdev->vma_manager);
>   	write_unlock(&bdev->vm_lock);
>   
>   	return ret;
> @@ -1454,9 +1450,8 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
>   	if (unlikely(ret != 0))
>   		goto out_no_sys;
>   
> -	bdev->addr_space_rb = RB_ROOT;
> -	drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000);
> -
> +	drm_vma_offset_manager_init(&bdev->vma_manager, file_page_offset,
> +				    0x10000000);
>   	INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue);
>   	INIT_LIST_HEAD(&bdev->ddestroy);
>   	bdev->dev_mapping = NULL;
> @@ -1498,12 +1493,17 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
>   void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo)
>   {
>   	struct ttm_bo_device *bdev = bo->bdev;
> -	loff_t offset = (loff_t) bo->addr_space_offset;
> -	loff_t holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
> +	loff_t offset, holelen;
>   
>   	if (!bdev->dev_mapping)
>   		return;
> -	unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
> +
> +	if (drm_vma_node_has_offset(&bo->vma_node)) {
> +		offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node);
> +		holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
> +
> +		unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
> +	}
>   	ttm_mem_io_free_vm(bo);
>   }
>   
> @@ -1520,31 +1520,6 @@ void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo)
>   
>   EXPORT_SYMBOL(ttm_bo_unmap_virtual);
>   
> -static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo)
> -{
> -	struct ttm_bo_device *bdev = bo->bdev;
> -	struct rb_node **cur = &bdev->addr_space_rb.rb_node;
> -	struct rb_node *parent = NULL;
> -	struct ttm_buffer_object *cur_bo;
> -	unsigned long offset = bo->vm_node->start;
> -	unsigned long cur_offset;
> -
> -	while (*cur) {
> -		parent = *cur;
> -		cur_bo = rb_entry(parent, struct ttm_buffer_object, vm_rb);
> -		cur_offset = cur_bo->vm_node->start;
> -		if (offset < cur_offset)
> -			cur = &parent->rb_left;
> -		else if (offset > cur_offset)
> -			cur = &parent->rb_right;
> -		else
> -			BUG();
> -	}
> -
> -	rb_link_node(&bo->vm_rb, parent, cur);
> -	rb_insert_color(&bo->vm_rb, &bdev->addr_space_rb);
> -}
> -
>   /**
>    * ttm_bo_setup_vm:
>    *
> @@ -1559,38 +1534,9 @@ static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo)
>   static int ttm_bo_setup_vm(struct ttm_buffer_object *bo)
>   {
>   	struct ttm_bo_device *bdev = bo->bdev;
> -	int ret;
> -
> -retry_pre_get:
> -	ret = drm_mm_pre_get(&bdev->addr_space_mm);
> -	if (unlikely(ret != 0))
> -		return ret;
>   
> -	write_lock(&bdev->vm_lock);
> -	bo->vm_node = drm_mm_search_free(&bdev->addr_space_mm,
> -					 bo->mem.num_pages, 0, 0);
> -
> -	if (unlikely(bo->vm_node == NULL)) {
> -		ret = -ENOMEM;
> -		goto out_unlock;
> -	}
> -
> -	bo->vm_node = drm_mm_get_block_atomic(bo->vm_node,
> -					      bo->mem.num_pages, 0);
> -
> -	if (unlikely(bo->vm_node == NULL)) {
> -		write_unlock(&bdev->vm_lock);
> -		goto retry_pre_get;
> -	}
> -
> -	ttm_bo_vm_insert_rb(bo);
> -	write_unlock(&bdev->vm_lock);
> -	bo->addr_space_offset = ((uint64_t) bo->vm_node->start) << PAGE_SHIFT;
> -
> -	return 0;
> -out_unlock:
> -	write_unlock(&bdev->vm_lock);
> -	return ret;
> +	return drm_vma_offset_add(&bdev->vma_manager, &bo->vma_node,
> +				  bo->mem.num_pages);
>   }
>   
>   int ttm_bo_wait(struct ttm_buffer_object *bo,
> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
> index 319cf41..7cc904d 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> @@ -30,6 +30,7 @@
>   
>   #include <drm/ttm/ttm_bo_driver.h>
>   #include <drm/ttm/ttm_placement.h>
> +#include <drm/drm_vma_manager.h>
>   #include <linux/io.h>
>   #include <linux/highmem.h>
>   #include <linux/wait.h>
> @@ -450,7 +451,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
>   	INIT_LIST_HEAD(&fbo->lru);
>   	INIT_LIST_HEAD(&fbo->swap);
>   	INIT_LIST_HEAD(&fbo->io_reserve_lru);
> -	fbo->vm_node = NULL;
> +	drm_vma_node_reset(&fbo->vma_node);
>   	atomic_set(&fbo->cpu_writers, 0);
>   
>   	spin_lock(&bdev->fence_lock);
> diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
> index 3df9f16..54a67f1 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
> @@ -33,6 +33,7 @@
>   #include <ttm/ttm_module.h>
>   #include <ttm/ttm_bo_driver.h>
>   #include <ttm/ttm_placement.h>
> +#include <drm/drm_vma_manager.h>
>   #include <linux/mm.h>
>   #include <linux/rbtree.h>
>   #include <linux/module.h>
> @@ -40,37 +41,6 @@
>   
>   #define TTM_BO_VM_NUM_PREFAULT 16
>   
> -static struct ttm_buffer_object *ttm_bo_vm_lookup_rb(struct ttm_bo_device *bdev,
> -						     unsigned long page_start,
> -						     unsigned long num_pages)
> -{
> -	struct rb_node *cur = bdev->addr_space_rb.rb_node;
> -	unsigned long cur_offset;
> -	struct ttm_buffer_object *bo;
> -	struct ttm_buffer_object *best_bo = NULL;
> -
> -	while (likely(cur != NULL)) {
> -		bo = rb_entry(cur, struct ttm_buffer_object, vm_rb);
> -		cur_offset = bo->vm_node->start;
> -		if (page_start >= cur_offset) {
> -			cur = cur->rb_right;
> -			best_bo = bo;
> -			if (page_start == cur_offset)
> -				break;
> -		} else
> -			cur = cur->rb_left;
> -	}
> -
> -	if (unlikely(best_bo == NULL))
> -		return NULL;
> -
> -	if (unlikely((best_bo->vm_node->start + best_bo->num_pages) <
> -		     (page_start + num_pages)))
> -		return NULL;
> -
> -	return best_bo;
> -}
> -
>   static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
>   {
>   	struct ttm_buffer_object *bo = (struct ttm_buffer_object *)
> @@ -146,9 +116,9 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
>   	}
>   
>   	page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) +
> -	    bo->vm_node->start - vma->vm_pgoff;
> +	    drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
>   	page_last = vma_pages(vma) +
> -	    bo->vm_node->start - vma->vm_pgoff;
> +	    drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
>   
>   	if (unlikely(page_offset >= bo->num_pages)) {
>   		retval = VM_FAULT_SIGBUS;
> @@ -249,6 +219,30 @@ static const struct vm_operations_struct ttm_bo_vm_ops = {
>   	.close = ttm_bo_vm_close
>   };
>   
> +static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device *bdev,
> +						  unsigned long offset,
> +						  unsigned long pages)
> +{
> +	struct drm_vma_offset_node *node;
> +	struct ttm_buffer_object *bo = NULL;
> +
> +	read_lock(&bdev->vm_lock);
> +
> +	node = drm_vma_offset_lookup(&bdev->vma_manager, offset, pages);
> +	if (likely(node)) {
> +		bo = container_of(node, struct ttm_buffer_object, vma_node);
> +		if (!kref_get_unless_zero(&bo->kref))
> +			bo = NULL;
> +	}
> +
> +	read_unlock(&bdev->vm_lock);
> +
> +	if (!bo)
> +		pr_err("Could not find buffer object to map\n");
> +
> +	return bo;
> +}
> +
>   int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
>   		struct ttm_bo_device *bdev)
>   {
> @@ -256,17 +250,9 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
>   	struct ttm_buffer_object *bo;
>   	int ret;
>   
> -	read_lock(&bdev->vm_lock);
> -	bo = ttm_bo_vm_lookup_rb(bdev, vma->vm_pgoff,
> -				 vma_pages(vma));
> -	if (likely(bo != NULL) && !kref_get_unless_zero(&bo->kref))
> -		bo = NULL;
> -	read_unlock(&bdev->vm_lock);
> -
> -	if (unlikely(bo == NULL)) {
> -		pr_err("Could not find buffer object to map\n");
> +	bo = ttm_bo_vm_lookup(bdev, vma->vm_pgoff, vma_pages(vma));
> +	if (unlikely(!bo))
>   		return -EINVAL;
> -	}
>   
>   	driver = bo->bdev->driver;
>   	if (unlikely(!driver->verify_access)) {
> @@ -324,12 +310,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
>   	bool no_wait = false;
>   	bool dummy;
>   
> -	read_lock(&bdev->vm_lock);
> -	bo = ttm_bo_vm_lookup_rb(bdev, dev_offset, 1);
> -	if (likely(bo != NULL))
> -		ttm_bo_reference(bo);
> -	read_unlock(&bdev->vm_lock);
> -
> +	bo = ttm_bo_vm_lookup(bdev, dev_offset, 1);
>   	if (unlikely(bo == NULL))
>   		return -EFAULT;
>   
> @@ -343,7 +324,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
>   	if (unlikely(ret != 0))
>   		goto out_unref;
>   
> -	kmap_offset = dev_offset - bo->vm_node->start;
> +	kmap_offset = dev_offset - drm_vma_node_start(&bo->vma_node);
>   	if (unlikely(kmap_offset >= bo->num_pages)) {
>   		ret = -EFBIG;
>   		goto out_unref;
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
> index 7953d1f..0e67cf4 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
> @@ -500,7 +500,7 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
>   		goto out_no_dmabuf;
>   
>   	rep->handle = handle;
> -	rep->map_handle = dma_buf->base.addr_space_offset;
> +	rep->map_handle = drm_vma_node_offset_addr(&dma_buf->base.vma_node);
>   	rep->cur_gmr_id = handle;
>   	rep->cur_gmr_offset = 0;
>   
> @@ -834,7 +834,7 @@ int vmw_dumb_map_offset(struct drm_file *file_priv,
>   	if (ret != 0)
>   		return -EINVAL;
>   
> -	*offset = out_buf->base.addr_space_offset;
> +	*offset = drm_vma_node_offset_addr(&out_buf->base.vma_node);
>   	vmw_dmabuf_unreference(&out_buf);
>   	return 0;
>   }
> diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
> index 8a6aa56..751eaff 100644
> --- a/include/drm/ttm/ttm_bo_api.h
> +++ b/include/drm/ttm/ttm_bo_api.h
> @@ -32,12 +32,12 @@
>   #define _TTM_BO_API_H_
>   
>   #include <drm/drm_hashtab.h>
> +#include <drm/drm_vma_manager.h>
>   #include <linux/kref.h>
>   #include <linux/list.h>
>   #include <linux/wait.h>
>   #include <linux/mutex.h>
>   #include <linux/mm.h>
> -#include <linux/rbtree.h>
>   #include <linux/bitmap.h>
>   #include <linux/reservation.h>
>   
> @@ -145,7 +145,6 @@ struct ttm_tt;
>    * @type: The bo type.
>    * @destroy: Destruction function. If NULL, kfree is used.
>    * @num_pages: Actual number of pages.
> - * @addr_space_offset: Address space offset.
>    * @acc_size: Accounted size for this object.
>    * @kref: Reference count of this buffer object. When this refcount reaches
>    * zero, the object is put on the delayed delete list.
> @@ -166,8 +165,7 @@ struct ttm_tt;
>    * @swap: List head for swap LRU list.
>    * @sync_obj: Pointer to a synchronization object.
>    * @priv_flags: Flags describing buffer object internal state.
> - * @vm_rb: Rb node for the vm rb tree.
> - * @vm_node: Address space manager node.
> + * @vma_node: Address space manager node.
>    * @offset: The current GPU offset, which can have different meanings
>    * depending on the memory type. For SYSTEM type memory, it should be 0.
>    * @cur_placement: Hint of current placement.
> @@ -194,7 +192,6 @@ struct ttm_buffer_object {
>   	enum ttm_bo_type type;
>   	void (*destroy) (struct ttm_buffer_object *);
>   	unsigned long num_pages;
> -	uint64_t addr_space_offset;
>   	size_t acc_size;
>   
>   	/**
> @@ -238,13 +235,7 @@ struct ttm_buffer_object {
>   	void *sync_obj;
>   	unsigned long priv_flags;
>   
> -	/**
> -	 * Members protected by the bdev::vm_lock
> -	 */
> -
> -	struct rb_node vm_rb;
> -	struct drm_mm_node *vm_node;
> -
> +	struct drm_vma_offset_node vma_node;
>   
>   	/**
>   	 * Special members that are protected by the reserve lock
> diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
> index 984fc2d..d3b8479 100644
> --- a/include/drm/ttm/ttm_bo_driver.h
> +++ b/include/drm/ttm/ttm_bo_driver.h
> @@ -36,6 +36,7 @@
>   #include <ttm/ttm_placement.h>
>   #include <drm/drm_mm.h>
>   #include <drm/drm_global.h>
> +#include <drm/drm_vma_manager.h>
>   #include <linux/workqueue.h>
>   #include <linux/fs.h>
>   #include <linux/spinlock.h>
> @@ -519,7 +520,7 @@ struct ttm_bo_global {
>    * @man: An array of mem_type_managers.
>    * @fence_lock: Protects the synchronizing members on *all* bos belonging
>    * to this device.
> - * @addr_space_mm: Range manager for the device address space.
> + * @vma_manager: Address space manager
>    * lru_lock: Spinlock that protects the buffer+device lru lists and
>    * ddestroy lists.
>    * @val_seq: Current validation sequence.
> @@ -540,11 +541,11 @@ struct ttm_bo_device {
>   	rwlock_t vm_lock;
>   	struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES];
>   	spinlock_t fence_lock;
> +
>   	/*
>   	 * Protected by the vm lock.
>   	 */
> -	struct rb_root addr_space_rb;
> -	struct drm_mm addr_space_mm;
> +	struct drm_vma_offset_manager vma_manager;
>   
>   	/*
>   	 * Protected by the global:lru lock

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-18  8:53   ` Thomas Hellstrom
@ 2013-07-18 11:02     ` Daniel Vetter
  2013-07-18 11:07     ` David Herrmann
  1 sibling, 0 replies; 31+ messages in thread
From: Daniel Vetter @ 2013-07-18 11:02 UTC (permalink / raw)
  To: Thomas Hellstrom
  Cc: Martin Peres, dri-devel, Ben Skeggs, Alex Deucher, Dave Airlie

On Thu, Jul 18, 2013 at 10:53 AM, Thomas Hellstrom
<thellstrom@vmware.com> wrote:
> A quick look, but not a full review:
>
> Looks mostly good, but it looks like the TTM vm lock isn't needed at all
> anymore (provided the vma offset manager is properly protected), since
> kref_get_unless_zero() is used when a reference after lookup is taken.
> (please see the kref_get_unless_zero documentation examples). When
> drm_vma_offset_remove() is called, the kref value is unconditionally zero,
> and that should block any successful lookup.
>
> Otherwise, if the vma offset manager always needs external locking to make
> lookup + referencing atomic, then perhaps locking should be completely
> left to the caller?

Somehow I've thought plain gem drivers had this fixed, but looks like
I've never gotten around to it. So we need to rework things anyway.

What about a drm_vma_offset_lookup_unlocked which just checks that the
caller is holding the offset manager's lock? That way everyone can
follow up with the get_unless_zero dance correctly. And ttm could drop
it's own vm lock.

I've considered whether we should just move the vma node into struct
drm_gem_object and let the offset manager do the dance, but that's
much more invasive. And I'm not sure it's the right thing to do yet.
So I think we should consider this only as a follow-up series.
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-18  8:53   ` Thomas Hellstrom
  2013-07-18 11:02     ` Daniel Vetter
@ 2013-07-18 11:07     ` David Herrmann
  2013-07-18 11:24       ` Thomas Hellstrom
  1 sibling, 1 reply; 31+ messages in thread
From: David Herrmann @ 2013-07-18 11:07 UTC (permalink / raw)
  To: Thomas Hellstrom
  Cc: Martin Peres, dri-devel, Alex Deucher, Ben Skeggs, Dave Airlie

Hi

On Thu, Jul 18, 2013 at 10:53 AM, Thomas Hellstrom
<thellstrom@vmware.com> wrote:
> A quick look, but not a full review:
>
> Looks mostly good, but it looks like the TTM vm lock isn't needed at all
> anymore (provided the vma offset manager is properly protected), since
> kref_get_unless_zero() is used when a reference after lookup is taken.
> (please see the kref_get_unless_zero documentation examples). When
> drm_vma_offset_remove() is called, the kref value is unconditionally zero,
> and that should block any successful lookup.

If we drop vm_lock without modifying TTM, this will not work. Even
kref_get_unless_zero() needs some guarantee that the object is still
valid. Assume this scenario:

drm_vma_offset_lookup() returns a valid node, however, before we call
kref_get_*(), another thread destroys the last reference to the bo so
it gets kfree()d. kref_get_unless_zero() will thus reference memory
which can be _anything_ and is not guarantee to stay 0.
(Documentation/kref.txt mentions this, too, and I guess it was you who
wrote that).

I cannot see any locking around the mmap call that could guarantee
this except vm_lock.

> Otherwise, if the vma offset manager always needs external locking to make
> lookup + referencing atomic, then perhaps locking should be completely
> left to the caller?

I would actually prefer to keep it in the VMA manager. It allows some
fast-paths which otherwise would need special semantics for the caller
(for instance see the access-management patches which are based on
this patchset or the reduced locking during setup/release). We'd also
require the caller to use rwsems for good performance, which is not
the case for gem.

So how about Daniel's proposal to add an _unlocked() version and
provide _lock_lookup() and _unlock_lookup() helpers which just wrap
the read_lock() and read_unlock?

Thanks!
David

> Thanks,
> Thomas
>
>
>
>
> On 07/17/2013 08:14 PM, David Herrmann wrote:
>>
>> Use the new vma-manager infrastructure. This doesn't change any
>> implementation details as the vma-offset-manager is nearly copied 1-to-1
>> from TTM.
>>
>> Even though the vma-manager uses its own locks, we still need bo->vm_lock
>> to prevent bos from being destroyed before we can get a reference during
>> lookup. However, this lock is not needed during vm-setup as we still
>> hold a reference there.
>>
>> This also drops the addr_space_offset member as it is a copy of vm_start
>> in vma_node objects. Use the accessor functions instead.
>>
>> Cc: Dave Airlie <airlied@redhat.com>
>> Cc: Ben Skeggs <bskeggs@redhat.com>
>> Cc: Maarten Lankhorst <maarten.lankhorst@canonical.com>
>> Cc: Martin Peres <martin.peres@labri.fr>
>> Cc: Alex Deucher <alexander.deucher@amd.com>
>> Cc: Thomas Hellstrom <thellstrom@vmware.com>
>> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
>> ---
>>   drivers/gpu/drm/ast/ast_main.c            |  2 +-
>>   drivers/gpu/drm/cirrus/cirrus_main.c      |  2 +-
>>   drivers/gpu/drm/mgag200/mgag200_main.c    |  2 +-
>>   drivers/gpu/drm/nouveau/nouveau_display.c |  2 +-
>>   drivers/gpu/drm/nouveau/nouveau_gem.c     |  2 +-
>>   drivers/gpu/drm/qxl/qxl_object.h          |  2 +-
>>   drivers/gpu/drm/qxl/qxl_release.c         |  2 +-
>>   drivers/gpu/drm/radeon/radeon_object.h    |  5 +-
>>   drivers/gpu/drm/ttm/ttm_bo.c              | 84
>> ++++++-------------------------
>>   drivers/gpu/drm/ttm/ttm_bo_util.c         |  3 +-
>>   drivers/gpu/drm/ttm/ttm_bo_vm.c           | 81
>> ++++++++++++-----------------
>>   drivers/gpu/drm/vmwgfx/vmwgfx_resource.c  |  4 +-
>>   include/drm/ttm/ttm_bo_api.h              | 15 ++----
>>   include/drm/ttm/ttm_bo_driver.h           |  7 +--
>>   14 files changed, 65 insertions(+), 148 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/ast/ast_main.c
>> b/drivers/gpu/drm/ast/ast_main.c
>> index f60fd7b..c195dc2 100644
>> --- a/drivers/gpu/drm/ast/ast_main.c
>> +++ b/drivers/gpu/drm/ast/ast_main.c
>> @@ -487,7 +487,7 @@ void ast_gem_free_object(struct drm_gem_object *obj)
>>     static inline u64 ast_bo_mmap_offset(struct ast_bo *bo)
>>   {
>> -       return bo->bo.addr_space_offset;
>> +       return drm_vma_node_offset_addr(&bo->bo.vma_node);
>>   }
>>   int
>>   ast_dumb_mmap_offset(struct drm_file *file,
>> diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c
>> b/drivers/gpu/drm/cirrus/cirrus_main.c
>> index 35cbae8..3a7a0ef 100644
>> --- a/drivers/gpu/drm/cirrus/cirrus_main.c
>> +++ b/drivers/gpu/drm/cirrus/cirrus_main.c
>> @@ -294,7 +294,7 @@ void cirrus_gem_free_object(struct drm_gem_object
>> *obj)
>>     static inline u64 cirrus_bo_mmap_offset(struct cirrus_bo *bo)
>>   {
>> -       return bo->bo.addr_space_offset;
>> +       return drm_vma_node_offset_addr(&bo->bo.vma_node);
>>   }
>>     int
>> diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c
>> b/drivers/gpu/drm/mgag200/mgag200_main.c
>> index 9fa5685..1a75ea3 100644
>> --- a/drivers/gpu/drm/mgag200/mgag200_main.c
>> +++ b/drivers/gpu/drm/mgag200/mgag200_main.c
>> @@ -349,7 +349,7 @@ void mgag200_gem_free_object(struct drm_gem_object
>> *obj)
>>     static inline u64 mgag200_bo_mmap_offset(struct mgag200_bo *bo)
>>   {
>> -       return bo->bo.addr_space_offset;
>> +       return drm_vma_node_offset_addr(&bo->bo.vma_node);
>>   }
>>     int
>> diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c
>> b/drivers/gpu/drm/nouveau/nouveau_display.c
>> index 708b2d1..7a8caa1 100644
>> --- a/drivers/gpu/drm/nouveau/nouveau_display.c
>> +++ b/drivers/gpu/drm/nouveau/nouveau_display.c
>> @@ -697,7 +697,7 @@ nouveau_display_dumb_map_offset(struct drm_file
>> *file_priv,
>>         gem = drm_gem_object_lookup(dev, file_priv, handle);
>>         if (gem) {
>>                 struct nouveau_bo *bo = gem->driver_private;
>> -               *poffset = bo->bo.addr_space_offset;
>> +               *poffset = drm_vma_node_offset_addr(&bo->bo.vma_node);
>>                 drm_gem_object_unreference_unlocked(gem);
>>                 return 0;
>>         }
>> diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c
>> b/drivers/gpu/drm/nouveau/nouveau_gem.c
>> index e72d09c..86597eb 100644
>> --- a/drivers/gpu/drm/nouveau/nouveau_gem.c
>> +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
>> @@ -226,7 +226,7 @@ nouveau_gem_info(struct drm_file *file_priv, struct
>> drm_gem_object *gem,
>>         }
>>         rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
>> -       rep->map_handle = nvbo->bo.addr_space_offset;
>> +       rep->map_handle = drm_vma_node_offset_addr(&nvbo->bo.vma_node);
>>         rep->tile_mode = nvbo->tile_mode;
>>         rep->tile_flags = nvbo->tile_flags;
>>         return 0;
>> diff --git a/drivers/gpu/drm/qxl/qxl_object.h
>> b/drivers/gpu/drm/qxl/qxl_object.h
>> index ee7ad79..af10165 100644
>> --- a/drivers/gpu/drm/qxl/qxl_object.h
>> +++ b/drivers/gpu/drm/qxl/qxl_object.h
>> @@ -59,7 +59,7 @@ static inline unsigned long qxl_bo_size(struct qxl_bo
>> *bo)
>>     static inline u64 qxl_bo_mmap_offset(struct qxl_bo *bo)
>>   {
>> -       return bo->tbo.addr_space_offset;
>> +       return drm_vma_node_offset_addr(&bo->tbo.vma_node);
>>   }
>>     static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type,
>> diff --git a/drivers/gpu/drm/qxl/qxl_release.c
>> b/drivers/gpu/drm/qxl/qxl_release.c
>> index b443d67..1a648e1 100644
>> --- a/drivers/gpu/drm/qxl/qxl_release.c
>> +++ b/drivers/gpu/drm/qxl/qxl_release.c
>> @@ -87,7 +87,7 @@ qxl_release_free(struct qxl_device *qdev,
>>         for (i = 0 ; i < release->bo_count; ++i) {
>>                 QXL_INFO(qdev, "release %llx\n",
>> -                       release->bos[i]->tbo.addr_space_offset
>> +
>> drm_vma_node_offset_addr(&release->bos[i]->tbo.vma_node)
>>                                                 - DRM_FILE_OFFSET);
>>                 qxl_fence_remove_release(&release->bos[i]->fence,
>> release->id);
>>                 qxl_bo_unref(&release->bos[i]);
>> diff --git a/drivers/gpu/drm/radeon/radeon_object.h
>> b/drivers/gpu/drm/radeon/radeon_object.h
>> index 91519a5..188c682 100644
>> --- a/drivers/gpu/drm/radeon/radeon_object.h
>> +++ b/drivers/gpu/drm/radeon/radeon_object.h
>> @@ -113,13 +113,10 @@ static inline unsigned
>> radeon_bo_gpu_page_alignment(struct radeon_bo *bo)
>>    * @bo:       radeon object for which we query the offset
>>    *
>>    * Returns mmap offset of the object.
>> - *
>> - * Note: addr_space_offset is constant after ttm bo init thus isn't
>> protected
>> - * by any lock.
>>    */
>>   static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo)
>>   {
>> -       return bo->tbo.addr_space_offset;
>> +       return drm_vma_node_offset_addr(&bo->tbo.vma_node);
>>   }
>>     extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
>> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
>> index cb9dd67..245850b 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo.c
>> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
>> @@ -616,11 +616,7 @@ static void ttm_bo_release(struct kref *kref)
>>         struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type];
>>         write_lock(&bdev->vm_lock);
>> -       if (likely(bo->vm_node != NULL)) {
>> -               rb_erase(&bo->vm_rb, &bdev->addr_space_rb);
>> -               drm_mm_put_block(bo->vm_node);
>> -               bo->vm_node = NULL;
>> -       }
>> +       drm_vma_offset_remove(&bdev->vma_manager, &bo->vma_node);
>>         write_unlock(&bdev->vm_lock);
>>         ttm_mem_io_lock(man, false);
>>         ttm_mem_io_free_vm(bo);
>> @@ -1129,6 +1125,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
>>         bo->resv = &bo->ttm_resv;
>>         reservation_object_init(bo->resv);
>>         atomic_inc(&bo->glob->bo_count);
>> +       drm_vma_node_reset(&bo->vma_node);
>>         ret = ttm_bo_check_placement(bo, placement);
>>   @@ -1424,9 +1421,8 @@ int ttm_bo_device_release(struct ttm_bo_device
>> *bdev)
>>                 TTM_DEBUG("Swap list was clean\n");
>>         spin_unlock(&glob->lru_lock);
>>   -     BUG_ON(!drm_mm_clean(&bdev->addr_space_mm));
>>         write_lock(&bdev->vm_lock);
>> -       drm_mm_takedown(&bdev->addr_space_mm);
>> +       drm_vma_offset_manager_destroy(&bdev->vma_manager);
>>         write_unlock(&bdev->vm_lock);
>>         return ret;
>> @@ -1454,9 +1450,8 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
>>         if (unlikely(ret != 0))
>>                 goto out_no_sys;
>>   -     bdev->addr_space_rb = RB_ROOT;
>> -       drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000);
>> -
>> +       drm_vma_offset_manager_init(&bdev->vma_manager, file_page_offset,
>> +                                   0x10000000);
>>         INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue);
>>         INIT_LIST_HEAD(&bdev->ddestroy);
>>         bdev->dev_mapping = NULL;
>> @@ -1498,12 +1493,17 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device
>> *bdev, struct ttm_mem_reg *mem)
>>   void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo)
>>   {
>>         struct ttm_bo_device *bdev = bo->bdev;
>> -       loff_t offset = (loff_t) bo->addr_space_offset;
>> -       loff_t holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
>> +       loff_t offset, holelen;
>>         if (!bdev->dev_mapping)
>>                 return;
>> -       unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
>> +
>> +       if (drm_vma_node_has_offset(&bo->vma_node)) {
>> +               offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node);
>> +               holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
>> +
>> +               unmap_mapping_range(bdev->dev_mapping, offset, holelen,
>> 1);
>> +       }
>>         ttm_mem_io_free_vm(bo);
>>   }
>>   @@ -1520,31 +1520,6 @@ void ttm_bo_unmap_virtual(struct
>> ttm_buffer_object *bo)
>>     EXPORT_SYMBOL(ttm_bo_unmap_virtual);
>>   -static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo)
>> -{
>> -       struct ttm_bo_device *bdev = bo->bdev;
>> -       struct rb_node **cur = &bdev->addr_space_rb.rb_node;
>> -       struct rb_node *parent = NULL;
>> -       struct ttm_buffer_object *cur_bo;
>> -       unsigned long offset = bo->vm_node->start;
>> -       unsigned long cur_offset;
>> -
>> -       while (*cur) {
>> -               parent = *cur;
>> -               cur_bo = rb_entry(parent, struct ttm_buffer_object,
>> vm_rb);
>> -               cur_offset = cur_bo->vm_node->start;
>> -               if (offset < cur_offset)
>> -                       cur = &parent->rb_left;
>> -               else if (offset > cur_offset)
>> -                       cur = &parent->rb_right;
>> -               else
>> -                       BUG();
>> -       }
>> -
>> -       rb_link_node(&bo->vm_rb, parent, cur);
>> -       rb_insert_color(&bo->vm_rb, &bdev->addr_space_rb);
>> -}
>> -
>>   /**
>>    * ttm_bo_setup_vm:
>>    *
>> @@ -1559,38 +1534,9 @@ static void ttm_bo_vm_insert_rb(struct
>> ttm_buffer_object *bo)
>>   static int ttm_bo_setup_vm(struct ttm_buffer_object *bo)
>>   {
>>         struct ttm_bo_device *bdev = bo->bdev;
>> -       int ret;
>> -
>> -retry_pre_get:
>> -       ret = drm_mm_pre_get(&bdev->addr_space_mm);
>> -       if (unlikely(ret != 0))
>> -               return ret;
>>   -     write_lock(&bdev->vm_lock);
>> -       bo->vm_node = drm_mm_search_free(&bdev->addr_space_mm,
>> -                                        bo->mem.num_pages, 0, 0);
>> -
>> -       if (unlikely(bo->vm_node == NULL)) {
>> -               ret = -ENOMEM;
>> -               goto out_unlock;
>> -       }
>> -
>> -       bo->vm_node = drm_mm_get_block_atomic(bo->vm_node,
>> -                                             bo->mem.num_pages, 0);
>> -
>> -       if (unlikely(bo->vm_node == NULL)) {
>> -               write_unlock(&bdev->vm_lock);
>> -               goto retry_pre_get;
>> -       }
>> -
>> -       ttm_bo_vm_insert_rb(bo);
>> -       write_unlock(&bdev->vm_lock);
>> -       bo->addr_space_offset = ((uint64_t) bo->vm_node->start) <<
>> PAGE_SHIFT;
>> -
>> -       return 0;
>> -out_unlock:
>> -       write_unlock(&bdev->vm_lock);
>> -       return ret;
>> +       return drm_vma_offset_add(&bdev->vma_manager, &bo->vma_node,
>> +                                 bo->mem.num_pages);
>>   }
>>     int ttm_bo_wait(struct ttm_buffer_object *bo,
>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
>> b/drivers/gpu/drm/ttm/ttm_bo_util.c
>> index 319cf41..7cc904d 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
>> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
>> @@ -30,6 +30,7 @@
>>     #include <drm/ttm/ttm_bo_driver.h>
>>   #include <drm/ttm/ttm_placement.h>
>> +#include <drm/drm_vma_manager.h>
>>   #include <linux/io.h>
>>   #include <linux/highmem.h>
>>   #include <linux/wait.h>
>> @@ -450,7 +451,7 @@ static int ttm_buffer_object_transfer(struct
>> ttm_buffer_object *bo,
>>         INIT_LIST_HEAD(&fbo->lru);
>>         INIT_LIST_HEAD(&fbo->swap);
>>         INIT_LIST_HEAD(&fbo->io_reserve_lru);
>> -       fbo->vm_node = NULL;
>> +       drm_vma_node_reset(&fbo->vma_node);
>>         atomic_set(&fbo->cpu_writers, 0);
>>         spin_lock(&bdev->fence_lock);
>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c
>> b/drivers/gpu/drm/ttm/ttm_bo_vm.c
>> index 3df9f16..54a67f1 100644
>> --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
>> +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
>> @@ -33,6 +33,7 @@
>>   #include <ttm/ttm_module.h>
>>   #include <ttm/ttm_bo_driver.h>
>>   #include <ttm/ttm_placement.h>
>> +#include <drm/drm_vma_manager.h>
>>   #include <linux/mm.h>
>>   #include <linux/rbtree.h>
>>   #include <linux/module.h>
>> @@ -40,37 +41,6 @@
>>     #define TTM_BO_VM_NUM_PREFAULT 16
>>   -static struct ttm_buffer_object *ttm_bo_vm_lookup_rb(struct
>> ttm_bo_device *bdev,
>> -                                                    unsigned long
>> page_start,
>> -                                                    unsigned long
>> num_pages)
>> -{
>> -       struct rb_node *cur = bdev->addr_space_rb.rb_node;
>> -       unsigned long cur_offset;
>> -       struct ttm_buffer_object *bo;
>> -       struct ttm_buffer_object *best_bo = NULL;
>> -
>> -       while (likely(cur != NULL)) {
>> -               bo = rb_entry(cur, struct ttm_buffer_object, vm_rb);
>> -               cur_offset = bo->vm_node->start;
>> -               if (page_start >= cur_offset) {
>> -                       cur = cur->rb_right;
>> -                       best_bo = bo;
>> -                       if (page_start == cur_offset)
>> -                               break;
>> -               } else
>> -                       cur = cur->rb_left;
>> -       }
>> -
>> -       if (unlikely(best_bo == NULL))
>> -               return NULL;
>> -
>> -       if (unlikely((best_bo->vm_node->start + best_bo->num_pages) <
>> -                    (page_start + num_pages)))
>> -               return NULL;
>> -
>> -       return best_bo;
>> -}
>> -
>>   static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault
>> *vmf)
>>   {
>>         struct ttm_buffer_object *bo = (struct ttm_buffer_object *)
>> @@ -146,9 +116,9 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma,
>> struct vm_fault *vmf)
>>         }
>>         page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) +
>> -           bo->vm_node->start - vma->vm_pgoff;
>> +           drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
>>         page_last = vma_pages(vma) +
>> -           bo->vm_node->start - vma->vm_pgoff;
>> +           drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
>>         if (unlikely(page_offset >= bo->num_pages)) {
>>                 retval = VM_FAULT_SIGBUS;
>> @@ -249,6 +219,30 @@ static const struct vm_operations_struct
>> ttm_bo_vm_ops = {
>>         .close = ttm_bo_vm_close
>>   };
>>   +static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device
>> *bdev,
>> +                                                 unsigned long offset,
>> +                                                 unsigned long pages)
>> +{
>> +       struct drm_vma_offset_node *node;
>> +       struct ttm_buffer_object *bo = NULL;
>> +
>> +       read_lock(&bdev->vm_lock);
>> +
>> +       node = drm_vma_offset_lookup(&bdev->vma_manager, offset, pages);
>> +       if (likely(node)) {
>> +               bo = container_of(node, struct ttm_buffer_object,
>> vma_node);
>> +               if (!kref_get_unless_zero(&bo->kref))
>> +                       bo = NULL;
>> +       }
>> +
>> +       read_unlock(&bdev->vm_lock);
>> +
>> +       if (!bo)
>> +               pr_err("Could not find buffer object to map\n");
>> +
>> +       return bo;
>> +}
>> +
>>   int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
>>                 struct ttm_bo_device *bdev)
>>   {
>> @@ -256,17 +250,9 @@ int ttm_bo_mmap(struct file *filp, struct
>> vm_area_struct *vma,
>>         struct ttm_buffer_object *bo;
>>         int ret;
>>   -     read_lock(&bdev->vm_lock);
>> -       bo = ttm_bo_vm_lookup_rb(bdev, vma->vm_pgoff,
>> -                                vma_pages(vma));
>> -       if (likely(bo != NULL) && !kref_get_unless_zero(&bo->kref))
>> -               bo = NULL;
>> -       read_unlock(&bdev->vm_lock);
>> -
>> -       if (unlikely(bo == NULL)) {
>> -               pr_err("Could not find buffer object to map\n");
>> +       bo = ttm_bo_vm_lookup(bdev, vma->vm_pgoff, vma_pages(vma));
>> +       if (unlikely(!bo))
>>                 return -EINVAL;
>> -       }
>>         driver = bo->bdev->driver;
>>         if (unlikely(!driver->verify_access)) {
>> @@ -324,12 +310,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct
>> file *filp,
>>         bool no_wait = false;
>>         bool dummy;
>>   -     read_lock(&bdev->vm_lock);
>> -       bo = ttm_bo_vm_lookup_rb(bdev, dev_offset, 1);
>> -       if (likely(bo != NULL))
>> -               ttm_bo_reference(bo);
>> -       read_unlock(&bdev->vm_lock);
>> -
>> +       bo = ttm_bo_vm_lookup(bdev, dev_offset, 1);
>>         if (unlikely(bo == NULL))
>>                 return -EFAULT;
>>   @@ -343,7 +324,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct
>> file *filp,
>>         if (unlikely(ret != 0))
>>                 goto out_unref;
>>   -     kmap_offset = dev_offset - bo->vm_node->start;
>> +       kmap_offset = dev_offset - drm_vma_node_start(&bo->vma_node);
>>         if (unlikely(kmap_offset >= bo->num_pages)) {
>>                 ret = -EFBIG;
>>                 goto out_unref;
>> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
>> b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
>> index 7953d1f..0e67cf4 100644
>> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
>> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
>> @@ -500,7 +500,7 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev,
>> void *data,
>>                 goto out_no_dmabuf;
>>         rep->handle = handle;
>> -       rep->map_handle = dma_buf->base.addr_space_offset;
>> +       rep->map_handle =
>> drm_vma_node_offset_addr(&dma_buf->base.vma_node);
>>         rep->cur_gmr_id = handle;
>>         rep->cur_gmr_offset = 0;
>>   @@ -834,7 +834,7 @@ int vmw_dumb_map_offset(struct drm_file *file_priv,
>>         if (ret != 0)
>>                 return -EINVAL;
>>   -     *offset = out_buf->base.addr_space_offset;
>> +       *offset = drm_vma_node_offset_addr(&out_buf->base.vma_node);
>>         vmw_dmabuf_unreference(&out_buf);
>>         return 0;
>>   }
>> diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
>> index 8a6aa56..751eaff 100644
>> --- a/include/drm/ttm/ttm_bo_api.h
>> +++ b/include/drm/ttm/ttm_bo_api.h
>> @@ -32,12 +32,12 @@
>>   #define _TTM_BO_API_H_
>>     #include <drm/drm_hashtab.h>
>> +#include <drm/drm_vma_manager.h>
>>   #include <linux/kref.h>
>>   #include <linux/list.h>
>>   #include <linux/wait.h>
>>   #include <linux/mutex.h>
>>   #include <linux/mm.h>
>> -#include <linux/rbtree.h>
>>   #include <linux/bitmap.h>
>>   #include <linux/reservation.h>
>>   @@ -145,7 +145,6 @@ struct ttm_tt;
>>    * @type: The bo type.
>>    * @destroy: Destruction function. If NULL, kfree is used.
>>    * @num_pages: Actual number of pages.
>> - * @addr_space_offset: Address space offset.
>>    * @acc_size: Accounted size for this object.
>>    * @kref: Reference count of this buffer object. When this refcount
>> reaches
>>    * zero, the object is put on the delayed delete list.
>> @@ -166,8 +165,7 @@ struct ttm_tt;
>>    * @swap: List head for swap LRU list.
>>    * @sync_obj: Pointer to a synchronization object.
>>    * @priv_flags: Flags describing buffer object internal state.
>> - * @vm_rb: Rb node for the vm rb tree.
>> - * @vm_node: Address space manager node.
>> + * @vma_node: Address space manager node.
>>    * @offset: The current GPU offset, which can have different meanings
>>    * depending on the memory type. For SYSTEM type memory, it should be 0.
>>    * @cur_placement: Hint of current placement.
>> @@ -194,7 +192,6 @@ struct ttm_buffer_object {
>>         enum ttm_bo_type type;
>>         void (*destroy) (struct ttm_buffer_object *);
>>         unsigned long num_pages;
>> -       uint64_t addr_space_offset;
>>         size_t acc_size;
>>         /**
>> @@ -238,13 +235,7 @@ struct ttm_buffer_object {
>>         void *sync_obj;
>>         unsigned long priv_flags;
>>   -     /**
>> -        * Members protected by the bdev::vm_lock
>> -        */
>> -
>> -       struct rb_node vm_rb;
>> -       struct drm_mm_node *vm_node;
>> -
>> +       struct drm_vma_offset_node vma_node;
>>         /**
>>          * Special members that are protected by the reserve lock
>> diff --git a/include/drm/ttm/ttm_bo_driver.h
>> b/include/drm/ttm/ttm_bo_driver.h
>> index 984fc2d..d3b8479 100644
>> --- a/include/drm/ttm/ttm_bo_driver.h
>> +++ b/include/drm/ttm/ttm_bo_driver.h
>> @@ -36,6 +36,7 @@
>>   #include <ttm/ttm_placement.h>
>>   #include <drm/drm_mm.h>
>>   #include <drm/drm_global.h>
>> +#include <drm/drm_vma_manager.h>
>>   #include <linux/workqueue.h>
>>   #include <linux/fs.h>
>>   #include <linux/spinlock.h>
>> @@ -519,7 +520,7 @@ struct ttm_bo_global {
>>    * @man: An array of mem_type_managers.
>>    * @fence_lock: Protects the synchronizing members on *all* bos
>> belonging
>>    * to this device.
>> - * @addr_space_mm: Range manager for the device address space.
>> + * @vma_manager: Address space manager
>>    * lru_lock: Spinlock that protects the buffer+device lru lists and
>>    * ddestroy lists.
>>    * @val_seq: Current validation sequence.
>> @@ -540,11 +541,11 @@ struct ttm_bo_device {
>>         rwlock_t vm_lock;
>>         struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES];
>>         spinlock_t fence_lock;
>> +
>>         /*
>>          * Protected by the vm lock.
>>          */
>> -       struct rb_root addr_space_rb;
>> -       struct drm_mm addr_space_mm;
>> +       struct drm_vma_offset_manager vma_manager;
>>         /*
>>          * Protected by the global:lru lock

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-18 11:07     ` David Herrmann
@ 2013-07-18 11:24       ` Thomas Hellstrom
  2013-07-18 20:54         ` David Herrmann
  0 siblings, 1 reply; 31+ messages in thread
From: Thomas Hellstrom @ 2013-07-18 11:24 UTC (permalink / raw)
  To: David Herrmann
  Cc: Martin Peres, dri-devel, Alex Deucher, Ben Skeggs, Dave Airlie

On 07/18/2013 01:07 PM, David Herrmann wrote:
> Hi
>
> On Thu, Jul 18, 2013 at 10:53 AM, Thomas Hellstrom
> <thellstrom@vmware.com> wrote:
>> A quick look, but not a full review:
>>
>> Looks mostly good, but it looks like the TTM vm lock isn't needed at all
>> anymore (provided the vma offset manager is properly protected), since
>> kref_get_unless_zero() is used when a reference after lookup is taken.
>> (please see the kref_get_unless_zero documentation examples). When
>> drm_vma_offset_remove() is called, the kref value is unconditionally zero,
>> and that should block any successful lookup.
> If we drop vm_lock without modifying TTM, this will not work. Even
> kref_get_unless_zero() needs some guarantee that the object is still
> valid. Assume this scenario:
>
> drm_vma_offset_lookup() returns a valid node, however, before we call
> kref_get_*(), another thread destroys the last reference to the bo so
> it gets kfree()d. kref_get_unless_zero() will thus reference memory
> which can be _anything_ and is not guarantee to stay 0.
> (Documentation/kref.txt mentions this, too, and I guess it was you who
> wrote that).
>
> I cannot see any locking around the mmap call that could guarantee
> this except vm_lock.

You're right. My apologies. Parental leave has taken its toll.

>
>> Otherwise, if the vma offset manager always needs external locking to make
>> lookup + referencing atomic, then perhaps locking should be completely
>> left to the caller?
> I would actually prefer to keep it in the VMA manager. It allows some
> fast-paths which otherwise would need special semantics for the caller
> (for instance see the access-management patches which are based on
> this patchset or the reduced locking during setup/release). We'd also
> require the caller to use rwsems for good performance, which is not
> the case for gem.
>
> So how about Daniel's proposal to add an _unlocked() version and
> provide _lock_lookup() and _unlock_lookup() helpers which just wrap
> the read_lock() and read_unlock?
>
> Thanks!
> David

I think that if there are good reasons to keep locking internal, I'm 
fine with that, (And also, of course, with
Daniel's proposal). Currently the add / remove / lookup paths are mostly 
used by TTM during object creation and
destruction.

However, if the lookup path is ever used by pread / pwrite, that 
situation might change and we would then like to
minimize the locking.

Thanks,
Thomas

>
>> Thanks,
>> Thomas
>>
>>
>>
>>
>> On 07/17/2013 08:14 PM, David Herrmann wrote:
>>> Use the new vma-manager infrastructure. This doesn't change any
>>> implementation details as the vma-offset-manager is nearly copied 1-to-1
>>> from TTM.
>>>
>>> Even though the vma-manager uses its own locks, we still need bo->vm_lock
>>> to prevent bos from being destroyed before we can get a reference during
>>> lookup. However, this lock is not needed during vm-setup as we still
>>> hold a reference there.
>>>
>>> This also drops the addr_space_offset member as it is a copy of vm_start
>>> in vma_node objects. Use the accessor functions instead.
>>>
>>> Cc: Dave Airlie <airlied@redhat.com>
>>> Cc: Ben Skeggs <bskeggs@redhat.com>
>>> Cc: Maarten Lankhorst <maarten.lankhorst@canonical.com>
>>> Cc: Martin Peres <martin.peres@labri.fr>
>>> Cc: Alex Deucher <alexander.deucher@amd.com>
>>> Cc: Thomas Hellstrom <thellstrom@vmware.com>
>>> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
>>> ---
>>>    drivers/gpu/drm/ast/ast_main.c            |  2 +-
>>>    drivers/gpu/drm/cirrus/cirrus_main.c      |  2 +-
>>>    drivers/gpu/drm/mgag200/mgag200_main.c    |  2 +-
>>>    drivers/gpu/drm/nouveau/nouveau_display.c |  2 +-
>>>    drivers/gpu/drm/nouveau/nouveau_gem.c     |  2 +-
>>>    drivers/gpu/drm/qxl/qxl_object.h          |  2 +-
>>>    drivers/gpu/drm/qxl/qxl_release.c         |  2 +-
>>>    drivers/gpu/drm/radeon/radeon_object.h    |  5 +-
>>>    drivers/gpu/drm/ttm/ttm_bo.c              | 84
>>> ++++++-------------------------
>>>    drivers/gpu/drm/ttm/ttm_bo_util.c         |  3 +-
>>>    drivers/gpu/drm/ttm/ttm_bo_vm.c           | 81
>>> ++++++++++++-----------------
>>>    drivers/gpu/drm/vmwgfx/vmwgfx_resource.c  |  4 +-
>>>    include/drm/ttm/ttm_bo_api.h              | 15 ++----
>>>    include/drm/ttm/ttm_bo_driver.h           |  7 +--
>>>    14 files changed, 65 insertions(+), 148 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/ast/ast_main.c
>>> b/drivers/gpu/drm/ast/ast_main.c
>>> index f60fd7b..c195dc2 100644
>>> --- a/drivers/gpu/drm/ast/ast_main.c
>>> +++ b/drivers/gpu/drm/ast/ast_main.c
>>> @@ -487,7 +487,7 @@ void ast_gem_free_object(struct drm_gem_object *obj)
>>>      static inline u64 ast_bo_mmap_offset(struct ast_bo *bo)
>>>    {
>>> -       return bo->bo.addr_space_offset;
>>> +       return drm_vma_node_offset_addr(&bo->bo.vma_node);
>>>    }
>>>    int
>>>    ast_dumb_mmap_offset(struct drm_file *file,
>>> diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c
>>> b/drivers/gpu/drm/cirrus/cirrus_main.c
>>> index 35cbae8..3a7a0ef 100644
>>> --- a/drivers/gpu/drm/cirrus/cirrus_main.c
>>> +++ b/drivers/gpu/drm/cirrus/cirrus_main.c
>>> @@ -294,7 +294,7 @@ void cirrus_gem_free_object(struct drm_gem_object
>>> *obj)
>>>      static inline u64 cirrus_bo_mmap_offset(struct cirrus_bo *bo)
>>>    {
>>> -       return bo->bo.addr_space_offset;
>>> +       return drm_vma_node_offset_addr(&bo->bo.vma_node);
>>>    }
>>>      int
>>> diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c
>>> b/drivers/gpu/drm/mgag200/mgag200_main.c
>>> index 9fa5685..1a75ea3 100644
>>> --- a/drivers/gpu/drm/mgag200/mgag200_main.c
>>> +++ b/drivers/gpu/drm/mgag200/mgag200_main.c
>>> @@ -349,7 +349,7 @@ void mgag200_gem_free_object(struct drm_gem_object
>>> *obj)
>>>      static inline u64 mgag200_bo_mmap_offset(struct mgag200_bo *bo)
>>>    {
>>> -       return bo->bo.addr_space_offset;
>>> +       return drm_vma_node_offset_addr(&bo->bo.vma_node);
>>>    }
>>>      int
>>> diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c
>>> b/drivers/gpu/drm/nouveau/nouveau_display.c
>>> index 708b2d1..7a8caa1 100644
>>> --- a/drivers/gpu/drm/nouveau/nouveau_display.c
>>> +++ b/drivers/gpu/drm/nouveau/nouveau_display.c
>>> @@ -697,7 +697,7 @@ nouveau_display_dumb_map_offset(struct drm_file
>>> *file_priv,
>>>          gem = drm_gem_object_lookup(dev, file_priv, handle);
>>>          if (gem) {
>>>                  struct nouveau_bo *bo = gem->driver_private;
>>> -               *poffset = bo->bo.addr_space_offset;
>>> +               *poffset = drm_vma_node_offset_addr(&bo->bo.vma_node);
>>>                  drm_gem_object_unreference_unlocked(gem);
>>>                  return 0;
>>>          }
>>> diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c
>>> b/drivers/gpu/drm/nouveau/nouveau_gem.c
>>> index e72d09c..86597eb 100644
>>> --- a/drivers/gpu/drm/nouveau/nouveau_gem.c
>>> +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
>>> @@ -226,7 +226,7 @@ nouveau_gem_info(struct drm_file *file_priv, struct
>>> drm_gem_object *gem,
>>>          }
>>>          rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
>>> -       rep->map_handle = nvbo->bo.addr_space_offset;
>>> +       rep->map_handle = drm_vma_node_offset_addr(&nvbo->bo.vma_node);
>>>          rep->tile_mode = nvbo->tile_mode;
>>>          rep->tile_flags = nvbo->tile_flags;
>>>          return 0;
>>> diff --git a/drivers/gpu/drm/qxl/qxl_object.h
>>> b/drivers/gpu/drm/qxl/qxl_object.h
>>> index ee7ad79..af10165 100644
>>> --- a/drivers/gpu/drm/qxl/qxl_object.h
>>> +++ b/drivers/gpu/drm/qxl/qxl_object.h
>>> @@ -59,7 +59,7 @@ static inline unsigned long qxl_bo_size(struct qxl_bo
>>> *bo)
>>>      static inline u64 qxl_bo_mmap_offset(struct qxl_bo *bo)
>>>    {
>>> -       return bo->tbo.addr_space_offset;
>>> +       return drm_vma_node_offset_addr(&bo->tbo.vma_node);
>>>    }
>>>      static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type,
>>> diff --git a/drivers/gpu/drm/qxl/qxl_release.c
>>> b/drivers/gpu/drm/qxl/qxl_release.c
>>> index b443d67..1a648e1 100644
>>> --- a/drivers/gpu/drm/qxl/qxl_release.c
>>> +++ b/drivers/gpu/drm/qxl/qxl_release.c
>>> @@ -87,7 +87,7 @@ qxl_release_free(struct qxl_device *qdev,
>>>          for (i = 0 ; i < release->bo_count; ++i) {
>>>                  QXL_INFO(qdev, "release %llx\n",
>>> -                       release->bos[i]->tbo.addr_space_offset
>>> +
>>> drm_vma_node_offset_addr(&release->bos[i]->tbo.vma_node)
>>>                                                  - DRM_FILE_OFFSET);
>>>                  qxl_fence_remove_release(&release->bos[i]->fence,
>>> release->id);
>>>                  qxl_bo_unref(&release->bos[i]);
>>> diff --git a/drivers/gpu/drm/radeon/radeon_object.h
>>> b/drivers/gpu/drm/radeon/radeon_object.h
>>> index 91519a5..188c682 100644
>>> --- a/drivers/gpu/drm/radeon/radeon_object.h
>>> +++ b/drivers/gpu/drm/radeon/radeon_object.h
>>> @@ -113,13 +113,10 @@ static inline unsigned
>>> radeon_bo_gpu_page_alignment(struct radeon_bo *bo)
>>>     * @bo:       radeon object for which we query the offset
>>>     *
>>>     * Returns mmap offset of the object.
>>> - *
>>> - * Note: addr_space_offset is constant after ttm bo init thus isn't
>>> protected
>>> - * by any lock.
>>>     */
>>>    static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo)
>>>    {
>>> -       return bo->tbo.addr_space_offset;
>>> +       return drm_vma_node_offset_addr(&bo->tbo.vma_node);
>>>    }
>>>      extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
>>> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
>>> index cb9dd67..245850b 100644
>>> --- a/drivers/gpu/drm/ttm/ttm_bo.c
>>> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
>>> @@ -616,11 +616,7 @@ static void ttm_bo_release(struct kref *kref)
>>>          struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type];
>>>          write_lock(&bdev->vm_lock);
>>> -       if (likely(bo->vm_node != NULL)) {
>>> -               rb_erase(&bo->vm_rb, &bdev->addr_space_rb);
>>> -               drm_mm_put_block(bo->vm_node);
>>> -               bo->vm_node = NULL;
>>> -       }
>>> +       drm_vma_offset_remove(&bdev->vma_manager, &bo->vma_node);
>>>          write_unlock(&bdev->vm_lock);
>>>          ttm_mem_io_lock(man, false);
>>>          ttm_mem_io_free_vm(bo);
>>> @@ -1129,6 +1125,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
>>>          bo->resv = &bo->ttm_resv;
>>>          reservation_object_init(bo->resv);
>>>          atomic_inc(&bo->glob->bo_count);
>>> +       drm_vma_node_reset(&bo->vma_node);
>>>          ret = ttm_bo_check_placement(bo, placement);
>>>    @@ -1424,9 +1421,8 @@ int ttm_bo_device_release(struct ttm_bo_device
>>> *bdev)
>>>                  TTM_DEBUG("Swap list was clean\n");
>>>          spin_unlock(&glob->lru_lock);
>>>    -     BUG_ON(!drm_mm_clean(&bdev->addr_space_mm));
>>>          write_lock(&bdev->vm_lock);
>>> -       drm_mm_takedown(&bdev->addr_space_mm);
>>> +       drm_vma_offset_manager_destroy(&bdev->vma_manager);
>>>          write_unlock(&bdev->vm_lock);
>>>          return ret;
>>> @@ -1454,9 +1450,8 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
>>>          if (unlikely(ret != 0))
>>>                  goto out_no_sys;
>>>    -     bdev->addr_space_rb = RB_ROOT;
>>> -       drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000);
>>> -
>>> +       drm_vma_offset_manager_init(&bdev->vma_manager, file_page_offset,
>>> +                                   0x10000000);
>>>          INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue);
>>>          INIT_LIST_HEAD(&bdev->ddestroy);
>>>          bdev->dev_mapping = NULL;
>>> @@ -1498,12 +1493,17 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device
>>> *bdev, struct ttm_mem_reg *mem)
>>>    void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo)
>>>    {
>>>          struct ttm_bo_device *bdev = bo->bdev;
>>> -       loff_t offset = (loff_t) bo->addr_space_offset;
>>> -       loff_t holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
>>> +       loff_t offset, holelen;
>>>          if (!bdev->dev_mapping)
>>>                  return;
>>> -       unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
>>> +
>>> +       if (drm_vma_node_has_offset(&bo->vma_node)) {
>>> +               offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node);
>>> +               holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
>>> +
>>> +               unmap_mapping_range(bdev->dev_mapping, offset, holelen,
>>> 1);
>>> +       }
>>>          ttm_mem_io_free_vm(bo);
>>>    }
>>>    @@ -1520,31 +1520,6 @@ void ttm_bo_unmap_virtual(struct
>>> ttm_buffer_object *bo)
>>>      EXPORT_SYMBOL(ttm_bo_unmap_virtual);
>>>    -static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo)
>>> -{
>>> -       struct ttm_bo_device *bdev = bo->bdev;
>>> -       struct rb_node **cur = &bdev->addr_space_rb.rb_node;
>>> -       struct rb_node *parent = NULL;
>>> -       struct ttm_buffer_object *cur_bo;
>>> -       unsigned long offset = bo->vm_node->start;
>>> -       unsigned long cur_offset;
>>> -
>>> -       while (*cur) {
>>> -               parent = *cur;
>>> -               cur_bo = rb_entry(parent, struct ttm_buffer_object,
>>> vm_rb);
>>> -               cur_offset = cur_bo->vm_node->start;
>>> -               if (offset < cur_offset)
>>> -                       cur = &parent->rb_left;
>>> -               else if (offset > cur_offset)
>>> -                       cur = &parent->rb_right;
>>> -               else
>>> -                       BUG();
>>> -       }
>>> -
>>> -       rb_link_node(&bo->vm_rb, parent, cur);
>>> -       rb_insert_color(&bo->vm_rb, &bdev->addr_space_rb);
>>> -}
>>> -
>>>    /**
>>>     * ttm_bo_setup_vm:
>>>     *
>>> @@ -1559,38 +1534,9 @@ static void ttm_bo_vm_insert_rb(struct
>>> ttm_buffer_object *bo)
>>>    static int ttm_bo_setup_vm(struct ttm_buffer_object *bo)
>>>    {
>>>          struct ttm_bo_device *bdev = bo->bdev;
>>> -       int ret;
>>> -
>>> -retry_pre_get:
>>> -       ret = drm_mm_pre_get(&bdev->addr_space_mm);
>>> -       if (unlikely(ret != 0))
>>> -               return ret;
>>>    -     write_lock(&bdev->vm_lock);
>>> -       bo->vm_node = drm_mm_search_free(&bdev->addr_space_mm,
>>> -                                        bo->mem.num_pages, 0, 0);
>>> -
>>> -       if (unlikely(bo->vm_node == NULL)) {
>>> -               ret = -ENOMEM;
>>> -               goto out_unlock;
>>> -       }
>>> -
>>> -       bo->vm_node = drm_mm_get_block_atomic(bo->vm_node,
>>> -                                             bo->mem.num_pages, 0);
>>> -
>>> -       if (unlikely(bo->vm_node == NULL)) {
>>> -               write_unlock(&bdev->vm_lock);
>>> -               goto retry_pre_get;
>>> -       }
>>> -
>>> -       ttm_bo_vm_insert_rb(bo);
>>> -       write_unlock(&bdev->vm_lock);
>>> -       bo->addr_space_offset = ((uint64_t) bo->vm_node->start) <<
>>> PAGE_SHIFT;
>>> -
>>> -       return 0;
>>> -out_unlock:
>>> -       write_unlock(&bdev->vm_lock);
>>> -       return ret;
>>> +       return drm_vma_offset_add(&bdev->vma_manager, &bo->vma_node,
>>> +                                 bo->mem.num_pages);
>>>    }
>>>      int ttm_bo_wait(struct ttm_buffer_object *bo,
>>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c
>>> b/drivers/gpu/drm/ttm/ttm_bo_util.c
>>> index 319cf41..7cc904d 100644
>>> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
>>> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
>>> @@ -30,6 +30,7 @@
>>>      #include <drm/ttm/ttm_bo_driver.h>
>>>    #include <drm/ttm/ttm_placement.h>
>>> +#include <drm/drm_vma_manager.h>
>>>    #include <linux/io.h>
>>>    #include <linux/highmem.h>
>>>    #include <linux/wait.h>
>>> @@ -450,7 +451,7 @@ static int ttm_buffer_object_transfer(struct
>>> ttm_buffer_object *bo,
>>>          INIT_LIST_HEAD(&fbo->lru);
>>>          INIT_LIST_HEAD(&fbo->swap);
>>>          INIT_LIST_HEAD(&fbo->io_reserve_lru);
>>> -       fbo->vm_node = NULL;
>>> +       drm_vma_node_reset(&fbo->vma_node);
>>>          atomic_set(&fbo->cpu_writers, 0);
>>>          spin_lock(&bdev->fence_lock);
>>> diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c
>>> b/drivers/gpu/drm/ttm/ttm_bo_vm.c
>>> index 3df9f16..54a67f1 100644
>>> --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
>>> +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
>>> @@ -33,6 +33,7 @@
>>>    #include <ttm/ttm_module.h>
>>>    #include <ttm/ttm_bo_driver.h>
>>>    #include <ttm/ttm_placement.h>
>>> +#include <drm/drm_vma_manager.h>
>>>    #include <linux/mm.h>
>>>    #include <linux/rbtree.h>
>>>    #include <linux/module.h>
>>> @@ -40,37 +41,6 @@
>>>      #define TTM_BO_VM_NUM_PREFAULT 16
>>>    -static struct ttm_buffer_object *ttm_bo_vm_lookup_rb(struct
>>> ttm_bo_device *bdev,
>>> -                                                    unsigned long
>>> page_start,
>>> -                                                    unsigned long
>>> num_pages)
>>> -{
>>> -       struct rb_node *cur = bdev->addr_space_rb.rb_node;
>>> -       unsigned long cur_offset;
>>> -       struct ttm_buffer_object *bo;
>>> -       struct ttm_buffer_object *best_bo = NULL;
>>> -
>>> -       while (likely(cur != NULL)) {
>>> -               bo = rb_entry(cur, struct ttm_buffer_object, vm_rb);
>>> -               cur_offset = bo->vm_node->start;
>>> -               if (page_start >= cur_offset) {
>>> -                       cur = cur->rb_right;
>>> -                       best_bo = bo;
>>> -                       if (page_start == cur_offset)
>>> -                               break;
>>> -               } else
>>> -                       cur = cur->rb_left;
>>> -       }
>>> -
>>> -       if (unlikely(best_bo == NULL))
>>> -               return NULL;
>>> -
>>> -       if (unlikely((best_bo->vm_node->start + best_bo->num_pages) <
>>> -                    (page_start + num_pages)))
>>> -               return NULL;
>>> -
>>> -       return best_bo;
>>> -}
>>> -
>>>    static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault
>>> *vmf)
>>>    {
>>>          struct ttm_buffer_object *bo = (struct ttm_buffer_object *)
>>> @@ -146,9 +116,9 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma,
>>> struct vm_fault *vmf)
>>>          }
>>>          page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) +
>>> -           bo->vm_node->start - vma->vm_pgoff;
>>> +           drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
>>>          page_last = vma_pages(vma) +
>>> -           bo->vm_node->start - vma->vm_pgoff;
>>> +           drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
>>>          if (unlikely(page_offset >= bo->num_pages)) {
>>>                  retval = VM_FAULT_SIGBUS;
>>> @@ -249,6 +219,30 @@ static const struct vm_operations_struct
>>> ttm_bo_vm_ops = {
>>>          .close = ttm_bo_vm_close
>>>    };
>>>    +static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device
>>> *bdev,
>>> +                                                 unsigned long offset,
>>> +                                                 unsigned long pages)
>>> +{
>>> +       struct drm_vma_offset_node *node;
>>> +       struct ttm_buffer_object *bo = NULL;
>>> +
>>> +       read_lock(&bdev->vm_lock);
>>> +
>>> +       node = drm_vma_offset_lookup(&bdev->vma_manager, offset, pages);
>>> +       if (likely(node)) {
>>> +               bo = container_of(node, struct ttm_buffer_object,
>>> vma_node);
>>> +               if (!kref_get_unless_zero(&bo->kref))
>>> +                       bo = NULL;
>>> +       }
>>> +
>>> +       read_unlock(&bdev->vm_lock);
>>> +
>>> +       if (!bo)
>>> +               pr_err("Could not find buffer object to map\n");
>>> +
>>> +       return bo;
>>> +}
>>> +
>>>    int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
>>>                  struct ttm_bo_device *bdev)
>>>    {
>>> @@ -256,17 +250,9 @@ int ttm_bo_mmap(struct file *filp, struct
>>> vm_area_struct *vma,
>>>          struct ttm_buffer_object *bo;
>>>          int ret;
>>>    -     read_lock(&bdev->vm_lock);
>>> -       bo = ttm_bo_vm_lookup_rb(bdev, vma->vm_pgoff,
>>> -                                vma_pages(vma));
>>> -       if (likely(bo != NULL) && !kref_get_unless_zero(&bo->kref))
>>> -               bo = NULL;
>>> -       read_unlock(&bdev->vm_lock);
>>> -
>>> -       if (unlikely(bo == NULL)) {
>>> -               pr_err("Could not find buffer object to map\n");
>>> +       bo = ttm_bo_vm_lookup(bdev, vma->vm_pgoff, vma_pages(vma));
>>> +       if (unlikely(!bo))
>>>                  return -EINVAL;
>>> -       }
>>>          driver = bo->bdev->driver;
>>>          if (unlikely(!driver->verify_access)) {
>>> @@ -324,12 +310,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct
>>> file *filp,
>>>          bool no_wait = false;
>>>          bool dummy;
>>>    -     read_lock(&bdev->vm_lock);
>>> -       bo = ttm_bo_vm_lookup_rb(bdev, dev_offset, 1);
>>> -       if (likely(bo != NULL))
>>> -               ttm_bo_reference(bo);
>>> -       read_unlock(&bdev->vm_lock);
>>> -
>>> +       bo = ttm_bo_vm_lookup(bdev, dev_offset, 1);
>>>          if (unlikely(bo == NULL))
>>>                  return -EFAULT;
>>>    @@ -343,7 +324,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct
>>> file *filp,
>>>          if (unlikely(ret != 0))
>>>                  goto out_unref;
>>>    -     kmap_offset = dev_offset - bo->vm_node->start;
>>> +       kmap_offset = dev_offset - drm_vma_node_start(&bo->vma_node);
>>>          if (unlikely(kmap_offset >= bo->num_pages)) {
>>>                  ret = -EFBIG;
>>>                  goto out_unref;
>>> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
>>> b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
>>> index 7953d1f..0e67cf4 100644
>>> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
>>> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
>>> @@ -500,7 +500,7 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev,
>>> void *data,
>>>                  goto out_no_dmabuf;
>>>          rep->handle = handle;
>>> -       rep->map_handle = dma_buf->base.addr_space_offset;
>>> +       rep->map_handle =
>>> drm_vma_node_offset_addr(&dma_buf->base.vma_node);
>>>          rep->cur_gmr_id = handle;
>>>          rep->cur_gmr_offset = 0;
>>>    @@ -834,7 +834,7 @@ int vmw_dumb_map_offset(struct drm_file *file_priv,
>>>          if (ret != 0)
>>>                  return -EINVAL;
>>>    -     *offset = out_buf->base.addr_space_offset;
>>> +       *offset = drm_vma_node_offset_addr(&out_buf->base.vma_node);
>>>          vmw_dmabuf_unreference(&out_buf);
>>>          return 0;
>>>    }
>>> diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
>>> index 8a6aa56..751eaff 100644
>>> --- a/include/drm/ttm/ttm_bo_api.h
>>> +++ b/include/drm/ttm/ttm_bo_api.h
>>> @@ -32,12 +32,12 @@
>>>    #define _TTM_BO_API_H_
>>>      #include <drm/drm_hashtab.h>
>>> +#include <drm/drm_vma_manager.h>
>>>    #include <linux/kref.h>
>>>    #include <linux/list.h>
>>>    #include <linux/wait.h>
>>>    #include <linux/mutex.h>
>>>    #include <linux/mm.h>
>>> -#include <linux/rbtree.h>
>>>    #include <linux/bitmap.h>
>>>    #include <linux/reservation.h>
>>>    @@ -145,7 +145,6 @@ struct ttm_tt;
>>>     * @type: The bo type.
>>>     * @destroy: Destruction function. If NULL, kfree is used.
>>>     * @num_pages: Actual number of pages.
>>> - * @addr_space_offset: Address space offset.
>>>     * @acc_size: Accounted size for this object.
>>>     * @kref: Reference count of this buffer object. When this refcount
>>> reaches
>>>     * zero, the object is put on the delayed delete list.
>>> @@ -166,8 +165,7 @@ struct ttm_tt;
>>>     * @swap: List head for swap LRU list.
>>>     * @sync_obj: Pointer to a synchronization object.
>>>     * @priv_flags: Flags describing buffer object internal state.
>>> - * @vm_rb: Rb node for the vm rb tree.
>>> - * @vm_node: Address space manager node.
>>> + * @vma_node: Address space manager node.
>>>     * @offset: The current GPU offset, which can have different meanings
>>>     * depending on the memory type. For SYSTEM type memory, it should be 0.
>>>     * @cur_placement: Hint of current placement.
>>> @@ -194,7 +192,6 @@ struct ttm_buffer_object {
>>>          enum ttm_bo_type type;
>>>          void (*destroy) (struct ttm_buffer_object *);
>>>          unsigned long num_pages;
>>> -       uint64_t addr_space_offset;
>>>          size_t acc_size;
>>>          /**
>>> @@ -238,13 +235,7 @@ struct ttm_buffer_object {
>>>          void *sync_obj;
>>>          unsigned long priv_flags;
>>>    -     /**
>>> -        * Members protected by the bdev::vm_lock
>>> -        */
>>> -
>>> -       struct rb_node vm_rb;
>>> -       struct drm_mm_node *vm_node;
>>> -
>>> +       struct drm_vma_offset_node vma_node;
>>>          /**
>>>           * Special members that are protected by the reserve lock
>>> diff --git a/include/drm/ttm/ttm_bo_driver.h
>>> b/include/drm/ttm/ttm_bo_driver.h
>>> index 984fc2d..d3b8479 100644
>>> --- a/include/drm/ttm/ttm_bo_driver.h
>>> +++ b/include/drm/ttm/ttm_bo_driver.h
>>> @@ -36,6 +36,7 @@
>>>    #include <ttm/ttm_placement.h>
>>>    #include <drm/drm_mm.h>
>>>    #include <drm/drm_global.h>
>>> +#include <drm/drm_vma_manager.h>
>>>    #include <linux/workqueue.h>
>>>    #include <linux/fs.h>
>>>    #include <linux/spinlock.h>
>>> @@ -519,7 +520,7 @@ struct ttm_bo_global {
>>>     * @man: An array of mem_type_managers.
>>>     * @fence_lock: Protects the synchronizing members on *all* bos
>>> belonging
>>>     * to this device.
>>> - * @addr_space_mm: Range manager for the device address space.
>>> + * @vma_manager: Address space manager
>>>     * lru_lock: Spinlock that protects the buffer+device lru lists and
>>>     * ddestroy lists.
>>>     * @val_seq: Current validation sequence.
>>> @@ -540,11 +541,11 @@ struct ttm_bo_device {
>>>          rwlock_t vm_lock;
>>>          struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES];
>>>          spinlock_t fence_lock;
>>> +
>>>          /*
>>>           * Protected by the vm lock.
>>>           */
>>> -       struct rb_root addr_space_rb;
>>> -       struct drm_mm addr_space_mm;
>>> +       struct drm_vma_offset_manager vma_manager;
>>>          /*
>>>           * Protected by the global:lru lock

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v3 2/4] drm/gem: convert to new unified vma manager
  2013-07-17 18:14 ` [PATCH v3 2/4] drm/gem: convert to new unified vma manager David Herrmann
@ 2013-07-18 19:01   ` Patrik Jakobsson
  0 siblings, 0 replies; 31+ messages in thread
From: Patrik Jakobsson @ 2013-07-18 19:01 UTC (permalink / raw)
  To: David Herrmann; +Cc: Daniel Vetter, dri-devel, Dave Airlie

On Wed, Jul 17, 2013 at 8:14 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
> Use the new vma manager instead of the old hashtable. Also convert all
> drivers to use the new convenience helpers. This drops all the
> (map_list.hash.key << PAGE_SHIFT) non-sense.
>
> Locking and access-management is exactly the same as before with an
> additional lock inside of the vma-manager, which strictly wouldn't be
> needed for gem.
>
> v2:
>  - rebase on drm-next
>  - init nodes via drm_vma_node_reset() in drm_gem.c
> v3:
>  - fix tegra
>
> Cc: Inki Dae <inki.dae@samsung.com>
> Cc: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Cc: Rob Clark <robdclark@gmail.com>
> Cc: Dave Airlie <airlied@redhat.com>
> Cc: Thierry Reding <thierry.reding@gmail.com>
> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
> ---
>  drivers/gpu/drm/drm_gem.c                  | 90 ++++++------------------------
>  drivers/gpu/drm/drm_gem_cma_helper.c       |  9 +--
>  drivers/gpu/drm/exynos/exynos_drm_gem.c    |  7 ++-
>  drivers/gpu/drm/gma500/gem.c               |  8 +--
>  drivers/gpu/drm/i915/i915_gem.c            |  9 +--
>  drivers/gpu/drm/omapdrm/omap_gem.c         | 11 ++--
>  drivers/gpu/drm/omapdrm/omap_gem_helpers.c | 49 +---------------
>  drivers/gpu/drm/udl/udl_gem.c              |  6 +-
>  drivers/gpu/host1x/drm/gem.c               |  4 +-
>  include/drm/drmP.h                         |  7 +--
>  include/uapi/drm/drm.h                     |  2 +-
>  11 files changed, 51 insertions(+), 151 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
> index 603f256..b5db89b 100644
> --- a/drivers/gpu/drm/drm_gem.c
> +++ b/drivers/gpu/drm/drm_gem.c
> @@ -37,6 +37,7 @@
>  #include <linux/shmem_fs.h>
>  #include <linux/dma-buf.h>
>  #include <drm/drmP.h>
> +#include <drm/drm_vma_manager.h>
>
>  /** @file drm_gem.c
>   *
> @@ -102,14 +103,9 @@ drm_gem_init(struct drm_device *dev)
>         }
>
>         dev->mm_private = mm;
> -
> -       if (drm_ht_create(&mm->offset_hash, 12)) {
> -               kfree(mm);
> -               return -ENOMEM;
> -       }
> -
> -       drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START,
> -                   DRM_FILE_PAGE_OFFSET_SIZE);
> +       drm_vma_offset_manager_init(&mm->vma_manager,
> +                                   DRM_FILE_PAGE_OFFSET_START,
> +                                   DRM_FILE_PAGE_OFFSET_SIZE);
>
>         return 0;
>  }
> @@ -119,8 +115,7 @@ drm_gem_destroy(struct drm_device *dev)
>  {
>         struct drm_gem_mm *mm = dev->mm_private;
>
> -       drm_mm_takedown(&mm->offset_manager);
> -       drm_ht_remove(&mm->offset_hash);
> +       drm_vma_offset_manager_destroy(&mm->vma_manager);
>         kfree(mm);
>         dev->mm_private = NULL;
>  }
> @@ -141,6 +136,7 @@ int drm_gem_object_init(struct drm_device *dev,
>
>         kref_init(&obj->refcount);
>         atomic_set(&obj->handle_count, 0);
> +       drm_vma_node_reset(&obj->vma_node);
>         obj->size = size;
>
>         return 0;
> @@ -162,6 +158,7 @@ int drm_gem_private_object_init(struct drm_device *dev,
>
>         kref_init(&obj->refcount);
>         atomic_set(&obj->handle_count, 0);
> +       drm_vma_node_reset(&obj->vma_node);
>         obj->size = size;
>
>         return 0;
> @@ -306,12 +303,8 @@ drm_gem_free_mmap_offset(struct drm_gem_object *obj)
>  {
>         struct drm_device *dev = obj->dev;
>         struct drm_gem_mm *mm = dev->mm_private;
> -       struct drm_map_list *list = &obj->map_list;
>
> -       drm_ht_remove_item(&mm->offset_hash, &list->hash);
> -       drm_mm_put_block(list->file_offset_node);
> -       kfree(list->map);
> -       list->map = NULL;
> +       drm_vma_offset_remove(&mm->vma_manager, &obj->vma_node);
>  }
>  EXPORT_SYMBOL(drm_gem_free_mmap_offset);
>
> @@ -331,54 +324,9 @@ drm_gem_create_mmap_offset(struct drm_gem_object *obj)
>  {
>         struct drm_device *dev = obj->dev;
>         struct drm_gem_mm *mm = dev->mm_private;
> -       struct drm_map_list *list;
> -       struct drm_local_map *map;
> -       int ret;
> -
> -       /* Set the object up for mmap'ing */
> -       list = &obj->map_list;
> -       list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
> -       if (!list->map)
> -               return -ENOMEM;
> -
> -       map = list->map;
> -       map->type = _DRM_GEM;
> -       map->size = obj->size;
> -       map->handle = obj;
> -
> -       /* Get a DRM GEM mmap offset allocated... */
> -       list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
> -                       obj->size / PAGE_SIZE, 0, false);
> -
> -       if (!list->file_offset_node) {
> -               DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
> -               ret = -ENOSPC;
> -               goto out_free_list;
> -       }
>
> -       list->file_offset_node = drm_mm_get_block(list->file_offset_node,
> -                       obj->size / PAGE_SIZE, 0);
> -       if (!list->file_offset_node) {
> -               ret = -ENOMEM;
> -               goto out_free_list;
> -       }
> -
> -       list->hash.key = list->file_offset_node->start;
> -       ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
> -       if (ret) {
> -               DRM_ERROR("failed to add to map hash\n");
> -               goto out_free_mm;
> -       }
> -
> -       return 0;
> -
> -out_free_mm:
> -       drm_mm_put_block(list->file_offset_node);
> -out_free_list:
> -       kfree(list->map);
> -       list->map = NULL;
> -
> -       return ret;
> +       return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node,
> +                                 obj->size / PAGE_SIZE);
>  }
>  EXPORT_SYMBOL(drm_gem_create_mmap_offset);
>
> @@ -707,8 +655,8 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
>         struct drm_file *priv = filp->private_data;
>         struct drm_device *dev = priv->minor->dev;
>         struct drm_gem_mm *mm = dev->mm_private;
> -       struct drm_local_map *map = NULL;
> -       struct drm_hash_item *hash;
> +       struct drm_gem_object *obj;
> +       struct drm_vma_offset_node *node;
>         int ret = 0;
>
>         if (drm_device_is_unplugged(dev))
> @@ -716,21 +664,15 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
>
>         mutex_lock(&dev->struct_mutex);
>
> -       if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
> +       node = drm_vma_offset_exact_lookup(&mm->vma_manager, vma->vm_pgoff);
> +       if (!node) {
>                 mutex_unlock(&dev->struct_mutex);
>                 return drm_mmap(filp, vma);
>         }
>
> -       map = drm_hash_entry(hash, struct drm_map_list, hash)->map;
> -       if (!map ||
> -           ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) {
> -               ret =  -EPERM;
> -               goto out_unlock;
> -       }
> -
> -       ret = drm_gem_mmap_obj(map->handle, map->size, vma);
> +       obj = container_of(node, struct drm_gem_object, vma_node);
> +       ret = drm_gem_mmap_obj(obj, node->vm_pages, vma);
>
> -out_unlock:
>         mutex_unlock(&dev->struct_mutex);
>
>         return ret;
> diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
> index ece72a8..3bf8212 100644
> --- a/drivers/gpu/drm/drm_gem_cma_helper.c
> +++ b/drivers/gpu/drm/drm_gem_cma_helper.c
> @@ -27,10 +27,11 @@
>  #include <drm/drmP.h>
>  #include <drm/drm.h>
>  #include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_vma_manager.h>
>
>  static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
>  {
> -       return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
> +       return (unsigned int)drm_vma_node_offset_addr(&obj->vma_node);
>  }
>
>  /*
> @@ -172,7 +173,7 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
>  {
>         struct drm_gem_cma_object *cma_obj;
>
> -       if (gem_obj->map_list.map)
> +       if (drm_vma_node_has_offset(&gem_obj->vma_node))
>                 drm_gem_free_mmap_offset(gem_obj);
>
>         cma_obj = to_drm_gem_cma_obj(gem_obj);
> @@ -305,8 +306,8 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m
>
>         WARN_ON(!mutex_is_locked(&dev->struct_mutex));
>
> -       if (obj->map_list.map)
> -               off = (uint64_t)obj->map_list.hash.key;
> +       if (drm_vma_node_has_offset(&obj->vma_node))
> +               off = drm_vma_node_start(&obj->vma_node);
>
>         seq_printf(m, "%2d (%2d) %08llx %08Zx %p %d",
>                         obj->name, obj->refcount.refcount.counter,
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
> index 24c22a8..a037871 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
> @@ -10,6 +10,7 @@
>   */
>
>  #include <drm/drmP.h>
> +#include <drm/drm_vma_manager.h>
>
>  #include <linux/shmem_fs.h>
>  #include <drm/exynos_drm.h>
> @@ -152,7 +153,7 @@ out:
>         exynos_drm_fini_buf(obj->dev, buf);
>         exynos_gem_obj->buffer = NULL;
>
> -       if (obj->map_list.map)
> +       if (drm_vma_node_has_offset(&obj->vma_node))
>                 drm_gem_free_mmap_offset(obj);
>
>         /* release file pointer to gem object. */
> @@ -703,13 +704,13 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
>                 goto unlock;
>         }
>
> -       if (!obj->map_list.map) {
> +       if (!drm_vma_node_has_offset(&obj->vma_node)) {
>                 ret = drm_gem_create_mmap_offset(obj);
>                 if (ret)
>                         goto out;
>         }
>
> -       *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
> +       *offset = drm_vma_node_offset_addr(&obj->vma_node);
>         DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
>
>  out:
> diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c
> index eefd6cc..9fd0c9f 100644
> --- a/drivers/gpu/drm/gma500/gem.c
> +++ b/drivers/gpu/drm/gma500/gem.c
> @@ -26,6 +26,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm.h>
>  #include <drm/gma_drm.h>
> +#include <drm/drm_vma_manager.h>
>  #include "psb_drv.h"
>
>  int psb_gem_init_object(struct drm_gem_object *obj)
> @@ -38,7 +39,7 @@ void psb_gem_free_object(struct drm_gem_object *obj)
>         struct gtt_range *gtt = container_of(obj, struct gtt_range, gem);
>
>         /* Remove the list map if one is present */
> -       if (obj->map_list.map)
> +       if (drm_vma_node_has_offset(&obj->vma_node))
>                 drm_gem_free_mmap_offset(obj);
>         drm_gem_object_release(obj);
>
> @@ -81,13 +82,12 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev,
>         /* What validation is needed here ? */
>
>         /* Make it mmapable */
> -       if (!obj->map_list.map) {
> +       if (!drm_vma_node_has_offset(&obj->vma_node)) {
>                 ret = drm_gem_create_mmap_offset(obj);
>                 if (ret)
>                         goto out;
>         }
> -       /* GEM should really work out the hash offsets for us */
> -       *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
> +       *offset = drm_vma_node_offset_addr(&obj->vma_node);
>  out:
>         drm_gem_object_unreference(obj);
>  unlock:
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 769f752..ca1ceb0 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -26,6 +26,7 @@
>   */
>
>  #include <drm/drmP.h>
> +#include <drm/drm_vma_manager.h>
>  #include <drm/i915_drm.h>
>  #include "i915_drv.h"
>  #include "i915_trace.h"
> @@ -1427,7 +1428,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
>
>         if (obj->base.dev->dev_mapping)
>                 unmap_mapping_range(obj->base.dev->dev_mapping,
> -                                   (loff_t)obj->base.map_list.hash.key<<PAGE_SHIFT,
> +                                   (loff_t)drm_vma_node_offset_addr(&obj->base.vma_node),
>                                     obj->base.size, 1);
>
>         obj->fault_mappable = false;
> @@ -1485,7 +1486,7 @@ static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj)
>         struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
>         int ret;
>
> -       if (obj->base.map_list.map)
> +       if (drm_vma_node_has_offset(&obj->base.vma_node))
>                 return 0;
>
>         dev_priv->mm.shrinker_no_lock_stealing = true;
> @@ -1516,7 +1517,7 @@ out:
>
>  static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj)
>  {
> -       if (!obj->base.map_list.map)
> +       if (!drm_vma_node_has_offset(&obj->base.vma_node))
>                 return;
>
>         drm_gem_free_mmap_offset(&obj->base);
> @@ -1557,7 +1558,7 @@ i915_gem_mmap_gtt(struct drm_file *file,
>         if (ret)
>                 goto out;
>
> -       *offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT;
> +       *offset = drm_vma_node_offset_addr(&obj->base.vma_node);
>
>  out:
>         drm_gem_object_unreference(&obj->base);
> diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
> index ebbdf41..0bcfdb9 100644
> --- a/drivers/gpu/drm/omapdrm/omap_gem.c
> +++ b/drivers/gpu/drm/omapdrm/omap_gem.c
> @@ -20,6 +20,7 @@
>
>  #include <linux/spinlock.h>
>  #include <linux/shmem_fs.h>
> +#include <drm/drm_vma_manager.h>
>
>  #include "omap_drv.h"
>  #include "omap_dmm_tiler.h"
> @@ -311,7 +312,7 @@ static uint64_t mmap_offset(struct drm_gem_object *obj)
>
>         WARN_ON(!mutex_is_locked(&dev->struct_mutex));
>
> -       if (!obj->map_list.map) {
> +       if (!drm_vma_node_has_offset(&obj->vma_node)) {
>                 /* Make it mmapable */
>                 size_t size = omap_gem_mmap_size(obj);
>                 int ret = _drm_gem_create_mmap_offset_size(obj, size);
> @@ -322,7 +323,7 @@ static uint64_t mmap_offset(struct drm_gem_object *obj)
>                 }
>         }
>
> -       return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT;
> +       return drm_vma_node_offset_addr(&obj->vma_node);
>  }
>
>  uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
> @@ -1001,8 +1002,8 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
>
>         WARN_ON(!mutex_is_locked(&dev->struct_mutex));
>
> -       if (obj->map_list.map)
> -               off = (uint64_t)obj->map_list.hash.key;
> +       if (drm_vma_node_has_offset(&obj->vma_node))
> +               off = drm_vma_node_start(&obj->vma_node);
>
>         seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d",
>                         omap_obj->flags, obj->name, obj->refcount.refcount.counter,
> @@ -1309,7 +1310,7 @@ void omap_gem_free_object(struct drm_gem_object *obj)
>
>         list_del(&omap_obj->mm_list);
>
> -       if (obj->map_list.map)
> +       if (drm_vma_node_has_offset(&obj->vma_node))
>                 drm_gem_free_mmap_offset(obj);
>
>         /* this means the object is still pinned.. which really should
> diff --git a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
> index f9eb679..dbb1575 100644
> --- a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
> +++ b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
> @@ -118,52 +118,7 @@ _drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size)
>  {
>         struct drm_device *dev = obj->dev;
>         struct drm_gem_mm *mm = dev->mm_private;
> -       struct drm_map_list *list;
> -       struct drm_local_map *map;
> -       int ret = 0;
> -
> -       /* Set the object up for mmap'ing */
> -       list = &obj->map_list;
> -       list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
> -       if (!list->map)
> -               return -ENOMEM;
> -
> -       map = list->map;
> -       map->type = _DRM_GEM;
> -       map->size = size;
> -       map->handle = obj;
> -
> -       /* Get a DRM GEM mmap offset allocated... */
> -       list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
> -                       size / PAGE_SIZE, 0, 0);
> -
> -       if (!list->file_offset_node) {
> -               DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
> -               ret = -ENOSPC;
> -               goto out_free_list;
> -       }
> -
> -       list->file_offset_node = drm_mm_get_block(list->file_offset_node,
> -                       size / PAGE_SIZE, 0);
> -       if (!list->file_offset_node) {
> -               ret = -ENOMEM;
> -               goto out_free_list;
> -       }
> -
> -       list->hash.key = list->file_offset_node->start;
> -       ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
> -       if (ret) {
> -               DRM_ERROR("failed to add to map hash\n");
> -               goto out_free_mm;
> -       }
> -
> -       return 0;
> -
> -out_free_mm:
> -       drm_mm_put_block(list->file_offset_node);
> -out_free_list:
> -       kfree(list->map);
> -       list->map = NULL;
>
> -       return ret;
> +       return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node,
> +                                 size / PAGE_SIZE);
>  }
> diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
> index ef034fa..59aee8a 100644
> --- a/drivers/gpu/drm/udl/udl_gem.c
> +++ b/drivers/gpu/drm/udl/udl_gem.c
> @@ -223,7 +223,7 @@ void udl_gem_free_object(struct drm_gem_object *gem_obj)
>         if (obj->pages)
>                 udl_gem_put_pages(obj);
>
> -       if (gem_obj->map_list.map)
> +       if (drm_vma_node_has_offset(&gem_obj->vma_node))
>                 drm_gem_free_mmap_offset(gem_obj);
>  }
>
> @@ -247,13 +247,13 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev,
>         ret = udl_gem_get_pages(gobj, GFP_KERNEL);
>         if (ret)
>                 goto out;
> -       if (!gobj->base.map_list.map) {
> +       if (!drm_vma_node_has_offset(&gobj->base.vma_node)) {
>                 ret = drm_gem_create_mmap_offset(obj);
>                 if (ret)
>                         goto out;
>         }
>
> -       *offset = (u64)gobj->base.map_list.hash.key << PAGE_SHIFT;
> +       *offset = drm_vma_node_offset_addr(&gobj->base.vma_node);
>
>  out:
>         drm_gem_object_unreference(&gobj->base);
> diff --git a/drivers/gpu/host1x/drm/gem.c b/drivers/gpu/host1x/drm/gem.c
> index c5e9a9b..1a0bf33 100644
> --- a/drivers/gpu/host1x/drm/gem.c
> +++ b/drivers/gpu/host1x/drm/gem.c
> @@ -108,7 +108,7 @@ static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo)
>
>  unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo)
>  {
> -       return (unsigned int)bo->gem.map_list.hash.key << PAGE_SHIFT;
> +       return (unsigned int)drm_vma_node_offset_addr(&bo->gem.vma_node);
>  }
>
>  struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
> @@ -182,7 +182,7 @@ void tegra_bo_free_object(struct drm_gem_object *gem)
>  {
>         struct tegra_bo *bo = to_tegra_bo(gem);
>
> -       if (gem->map_list.map)
> +       if (drm_vma_node_has_offset(&gem->vma_node))
>                 drm_gem_free_mmap_offset(gem);
>
>         drm_gem_object_release(gem);
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index 12083dc..93a5800 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -71,6 +71,7 @@
>  #include <asm/pgalloc.h>
>  #include <drm/drm.h>
>  #include <drm/drm_sarea.h>
> +#include <drm/drm_vma_manager.h>
>
>  #include <linux/idr.h>
>
> @@ -587,7 +588,6 @@ struct drm_map_list {
>         struct drm_local_map *map;      /**< mapping */
>         uint64_t user_token;
>         struct drm_master *master;
> -       struct drm_mm_node *file_offset_node;   /**< fake offset */
>  };
>
>  /**
> @@ -622,8 +622,7 @@ struct drm_ati_pcigart_info {
>   * GEM specific mm private for tracking GEM objects
>   */
>  struct drm_gem_mm {
> -       struct drm_mm offset_manager;   /**< Offset mgmt for buffer objects */
> -       struct drm_open_hash offset_hash; /**< User token hash table for maps */
> +       struct drm_vma_offset_manager vma_manager;
>  };
>
>  /**
> @@ -644,7 +643,7 @@ struct drm_gem_object {
>         struct file *filp;
>
>         /* Mapping info for this object */
> -       struct drm_map_list map_list;
> +       struct drm_vma_offset_node vma_node;
>
>         /**
>          * Size of the object, in bytes.  Immutable over the object's
> diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
> index 238a166..272580c 100644
> --- a/include/uapi/drm/drm.h
> +++ b/include/uapi/drm/drm.h
> @@ -181,7 +181,7 @@ enum drm_map_type {
>         _DRM_AGP = 3,             /**< AGP/GART */
>         _DRM_SCATTER_GATHER = 4,  /**< Scatter/gather memory for PCI DMA */
>         _DRM_CONSISTENT = 5,      /**< Consistent memory for PCI DMA */
> -       _DRM_GEM = 6,             /**< GEM object */
> +       _DRM_GEM = 6,             /**< GEM object (obsolete) */
>  };
>
>  /**
> --
> 1.8.3.2

Don't think I acked this before, so for the gma500 part:

Acked-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-18 11:24       ` Thomas Hellstrom
@ 2013-07-18 20:54         ` David Herrmann
  2013-07-19  2:03           ` Jerome Glisse
                             ` (2 more replies)
  0 siblings, 3 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-18 20:54 UTC (permalink / raw)
  To: Thomas Hellstrom
  Cc: Martin Peres, dri-devel, Alex Deucher, Ben Skeggs, Dave Airlie

Hi

On Thu, Jul 18, 2013 at 1:24 PM, Thomas Hellstrom <thellstrom@vmware.com> wrote:
> On 07/18/2013 01:07 PM, David Herrmann wrote:
>>
>> Hi
>>
>> On Thu, Jul 18, 2013 at 10:53 AM, Thomas Hellstrom
>> <thellstrom@vmware.com> wrote:
>>>
>>> A quick look, but not a full review:
>>>
>>> Looks mostly good, but it looks like the TTM vm lock isn't needed at all
>>> anymore (provided the vma offset manager is properly protected), since
>>> kref_get_unless_zero() is used when a reference after lookup is taken.
>>> (please see the kref_get_unless_zero documentation examples). When
>>> drm_vma_offset_remove() is called, the kref value is unconditionally
>>> zero,
>>> and that should block any successful lookup.
>>
>> If we drop vm_lock without modifying TTM, this will not work. Even
>> kref_get_unless_zero() needs some guarantee that the object is still
>> valid. Assume this scenario:
>>
>> drm_vma_offset_lookup() returns a valid node, however, before we call
>> kref_get_*(), another thread destroys the last reference to the bo so
>> it gets kfree()d. kref_get_unless_zero() will thus reference memory
>> which can be _anything_ and is not guarantee to stay 0.
>> (Documentation/kref.txt mentions this, too, and I guess it was you who
>> wrote that).
>>
>> I cannot see any locking around the mmap call that could guarantee
>> this except vm_lock.
>
>
> You're right. My apologies. Parental leave has taken its toll.
>
>
>>
>>> Otherwise, if the vma offset manager always needs external locking to
>>> make
>>> lookup + referencing atomic, then perhaps locking should be completely
>>> left to the caller?
>>
>> I would actually prefer to keep it in the VMA manager. It allows some
>> fast-paths which otherwise would need special semantics for the caller
>> (for instance see the access-management patches which are based on
>> this patchset or the reduced locking during setup/release). We'd also
>> require the caller to use rwsems for good performance, which is not
>> the case for gem.
>>
>> So how about Daniel's proposal to add an _unlocked() version and
>> provide _lock_lookup() and _unlock_lookup() helpers which just wrap
>> the read_lock() and read_unlock?
>>
>> Thanks!
>> David
>
>
> I think that if there are good reasons to keep locking internal, I'm fine
> with that, (And also, of course, with
> Daniel's proposal). Currently the add / remove / lookup paths are mostly
> used by TTM during object creation and
> destruction.
>
> However, if the lookup path is ever used by pread / pwrite, that situation
> might change and we would then like to
> minimize the locking.

I tried to keep the change as minimal as I could. Follow-up patches
are welcome. I just thought pushing the lock into drm_vma_* would
simplify things. If there are benchmarks that prove me wrong, I'll
gladly spend some time optimizing that.

Apart from this, I'd like to see someone ack the
ttm_buffer_object_transfer() changes. I am not very confident with
that. Everything else should be trivial.

Thanks
David

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-18 20:54         ` David Herrmann
@ 2013-07-19  2:03           ` Jerome Glisse
  2013-07-19  7:24           ` Maarten Lankhorst
  2013-07-19  9:13           ` Thomas Hellstrom
  2 siblings, 0 replies; 31+ messages in thread
From: Jerome Glisse @ 2013-07-19  2:03 UTC (permalink / raw)
  To: David Herrmann
  Cc: Thomas Hellstrom, Martin Peres, dri-devel, Ben Skeggs,
	Alex Deucher, Dave Airlie

On Thu, Jul 18, 2013 at 4:54 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
> Hi
>
> On Thu, Jul 18, 2013 at 1:24 PM, Thomas Hellstrom <thellstrom@vmware.com> wrote:
>> On 07/18/2013 01:07 PM, David Herrmann wrote:
>>>
>>> Hi
>>>
>>> On Thu, Jul 18, 2013 at 10:53 AM, Thomas Hellstrom
>>> <thellstrom@vmware.com> wrote:
>>>>
>>>> A quick look, but not a full review:
>>>>
>>>> Looks mostly good, but it looks like the TTM vm lock isn't needed at all
>>>> anymore (provided the vma offset manager is properly protected), since
>>>> kref_get_unless_zero() is used when a reference after lookup is taken.
>>>> (please see the kref_get_unless_zero documentation examples). When
>>>> drm_vma_offset_remove() is called, the kref value is unconditionally
>>>> zero,
>>>> and that should block any successful lookup.
>>>
>>> If we drop vm_lock without modifying TTM, this will not work. Even
>>> kref_get_unless_zero() needs some guarantee that the object is still
>>> valid. Assume this scenario:
>>>
>>> drm_vma_offset_lookup() returns a valid node, however, before we call
>>> kref_get_*(), another thread destroys the last reference to the bo so
>>> it gets kfree()d. kref_get_unless_zero() will thus reference memory
>>> which can be _anything_ and is not guarantee to stay 0.
>>> (Documentation/kref.txt mentions this, too, and I guess it was you who
>>> wrote that).
>>>
>>> I cannot see any locking around the mmap call that could guarantee
>>> this except vm_lock.
>>
>>
>> You're right. My apologies. Parental leave has taken its toll.
>>
>>
>>>
>>>> Otherwise, if the vma offset manager always needs external locking to
>>>> make
>>>> lookup + referencing atomic, then perhaps locking should be completely
>>>> left to the caller?
>>>
>>> I would actually prefer to keep it in the VMA manager. It allows some
>>> fast-paths which otherwise would need special semantics for the caller
>>> (for instance see the access-management patches which are based on
>>> this patchset or the reduced locking during setup/release). We'd also
>>> require the caller to use rwsems for good performance, which is not
>>> the case for gem.
>>>
>>> So how about Daniel's proposal to add an _unlocked() version and
>>> provide _lock_lookup() and _unlock_lookup() helpers which just wrap
>>> the read_lock() and read_unlock?
>>>
>>> Thanks!
>>> David
>>
>>
>> I think that if there are good reasons to keep locking internal, I'm fine
>> with that, (And also, of course, with
>> Daniel's proposal). Currently the add / remove / lookup paths are mostly
>> used by TTM during object creation and
>> destruction.
>>
>> However, if the lookup path is ever used by pread / pwrite, that situation
>> might change and we would then like to
>> minimize the locking.
>
> I tried to keep the change as minimal as I could. Follow-up patches
> are welcome. I just thought pushing the lock into drm_vma_* would
> simplify things. If there are benchmarks that prove me wrong, I'll
> gladly spend some time optimizing that.
>
> Apart from this, I'd like to see someone ack the
> ttm_buffer_object_transfer() changes. I am not very confident with
> that. Everything else should be trivial.
>
> Thanks
> David

Looks good to me, the transfer object must have an empty
vm_node/vma_node as we only interested in tying the system ram or vram
to the ghost object so that delayed delete the vram or system ram but
the original buffer is still valid and thus its original
vm_node/vma_node must not be free.

Cheers,
Jerome

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-18 20:54         ` David Herrmann
  2013-07-19  2:03           ` Jerome Glisse
@ 2013-07-19  7:24           ` Maarten Lankhorst
  2013-07-19  9:13           ` Thomas Hellstrom
  2 siblings, 0 replies; 31+ messages in thread
From: Maarten Lankhorst @ 2013-07-19  7:24 UTC (permalink / raw)
  To: David Herrmann
  Cc: Thomas Hellstrom, Martin Peres, dri-devel, Ben Skeggs,
	Alex Deucher, Dave Airlie

Op 18-07-13 22:54, David Herrmann schreef:
> Hi
>
> On Thu, Jul 18, 2013 at 1:24 PM, Thomas Hellstrom <thellstrom@vmware.com> wrote:
>> On 07/18/2013 01:07 PM, David Herrmann wrote:
>>> Hi
>>>
>>> On Thu, Jul 18, 2013 at 10:53 AM, Thomas Hellstrom
>>> <thellstrom@vmware.com> wrote:
>>>> A quick look, but not a full review:
>>>>
>>>> Looks mostly good, but it looks like the TTM vm lock isn't needed at all
>>>> anymore (provided the vma offset manager is properly protected), since
>>>> kref_get_unless_zero() is used when a reference after lookup is taken.
>>>> (please see the kref_get_unless_zero documentation examples). When
>>>> drm_vma_offset_remove() is called, the kref value is unconditionally
>>>> zero,
>>>> and that should block any successful lookup.
>>> If we drop vm_lock without modifying TTM, this will not work. Even
>>> kref_get_unless_zero() needs some guarantee that the object is still
>>> valid. Assume this scenario:
>>>
>>> drm_vma_offset_lookup() returns a valid node, however, before we call
>>> kref_get_*(), another thread destroys the last reference to the bo so
>>> it gets kfree()d. kref_get_unless_zero() will thus reference memory
>>> which can be _anything_ and is not guarantee to stay 0.
>>> (Documentation/kref.txt mentions this, too, and I guess it was you who
>>> wrote that).
>>>
>>> I cannot see any locking around the mmap call that could guarantee
>>> this except vm_lock.
>>
>> You're right. My apologies. Parental leave has taken its toll.
>>
>>
>>>> Otherwise, if the vma offset manager always needs external locking to
>>>> make
>>>> lookup + referencing atomic, then perhaps locking should be completely
>>>> left to the caller?
>>> I would actually prefer to keep it in the VMA manager. It allows some
>>> fast-paths which otherwise would need special semantics for the caller
>>> (for instance see the access-management patches which are based on
>>> this patchset or the reduced locking during setup/release). We'd also
>>> require the caller to use rwsems for good performance, which is not
>>> the case for gem.
>>>
>>> So how about Daniel's proposal to add an _unlocked() version and
>>> provide _lock_lookup() and _unlock_lookup() helpers which just wrap
>>> the read_lock() and read_unlock?
>>>
>>> Thanks!
>>> David
>>
>> I think that if there are good reasons to keep locking internal, I'm fine
>> with that, (And also, of course, with
>> Daniel's proposal). Currently the add / remove / lookup paths are mostly
>> used by TTM during object creation and
>> destruction.
>>
>> However, if the lookup path is ever used by pread / pwrite, that situation
>> might change and we would then like to
>> minimize the locking.
> I tried to keep the change as minimal as I could. Follow-up patches
> are welcome. I just thought pushing the lock into drm_vma_* would
> simplify things. If there are benchmarks that prove me wrong, I'll
> gladly spend some time optimizing that.
>
> Apart from this, I'd like to see someone ack the
> ttm_buffer_object_transfer() changes. I am not very confident with
> that. Everything else should be trivial.
>
The transfer object only exists to kill off the memory backing during asynchronous eviction,
by using the delayed destroyed mechanism. The re-initialization there looks correct to me.

~Maarten

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-18 20:54         ` David Herrmann
  2013-07-19  2:03           ` Jerome Glisse
  2013-07-19  7:24           ` Maarten Lankhorst
@ 2013-07-19  9:13           ` Thomas Hellstrom
       [not found]             ` <CANq1E4SSLtezptzo18TD3fLG_zoRwR17kMxEi96-e957F5wL1A@mail.gmail.com>
  2 siblings, 1 reply; 31+ messages in thread
From: Thomas Hellstrom @ 2013-07-19  9:13 UTC (permalink / raw)
  To: David Herrmann
  Cc: Martin Peres, dri-devel, Alex Deucher, Ben Skeggs, Dave Airlie

On 07/18/2013 10:54 PM, David Herrmann wrote:
> Hi
>
> On Thu, Jul 18, 2013 at 1:24 PM, Thomas Hellstrom <thellstrom@vmware.com> wrote:

...

>>
>> I think that if there are good reasons to keep locking internal, I'm fine
>> with that, (And also, of course, with
>> Daniel's proposal). Currently the add / remove / lookup paths are mostly
>> used by TTM during object creation and
>> destruction.
>>
>> However, if the lookup path is ever used by pread / pwrite, that situation
>> might change and we would then like to
>> minimize the locking.
> I tried to keep the change as minimal as I could. Follow-up patches
> are welcome. I just thought pushing the lock into drm_vma_* would
> simplify things. If there are benchmarks that prove me wrong, I'll
> gladly spend some time optimizing that.

In the general case, one reason for designing the locking outside of a 
utilities like this, is that different callers may have
different requirements. For example, the call path is known not to be 
multithreaded at all, or
the caller may prefer a mutex over a spinlock for various reasons. It 
might also be that some callers will want to use
RCU locking in the future if the lookup path becomes busy, and that 
would require *all* users to adapt to RCU object destruction...

I haven't looked at the code closely enough to say that any of this 
applies in this particular case, though.


Thanks,
Thomas


> Thanks
> David

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager
       [not found]             ` <CANq1E4SSLtezptzo18TD3fLG_zoRwR17kMxEi96-e957F5wL1A@mail.gmail.com>
@ 2013-07-22 10:55               ` David Herrmann
  2013-07-22 11:45                 ` Thomas Hellstrom
  0 siblings, 1 reply; 31+ messages in thread
From: David Herrmann @ 2013-07-22 10:55 UTC (permalink / raw)
  To: Thomas Hellstrom
  Cc: Martin Peres, dri-devel, Alex Deucher, Ben Skeggs, Dave Airlie

Sorry, I forgot to CC correctly.

On Mon, Jul 22, 2013 at 12:53 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
> Hi
>
> On Fri, Jul 19, 2013 at 11:13 AM, Thomas Hellstrom
> <thellstrom@vmware.com> wrote:
>> On 07/18/2013 10:54 PM, David Herrmann wrote:
>>>
>>> Hi
>>>
>>> On Thu, Jul 18, 2013 at 1:24 PM, Thomas Hellstrom <thellstrom@vmware.com>
>>> wrote:
>>
>>
>> ...
>>
>>
>>>>
>>>> I think that if there are good reasons to keep locking internal, I'm fine
>>>> with that, (And also, of course, with
>>>> Daniel's proposal). Currently the add / remove / lookup paths are mostly
>>>> used by TTM during object creation and
>>>> destruction.
>>>>
>>>> However, if the lookup path is ever used by pread / pwrite, that
>>>> situation
>>>> might change and we would then like to
>>>> minimize the locking.
>>>
>>> I tried to keep the change as minimal as I could. Follow-up patches
>>> are welcome. I just thought pushing the lock into drm_vma_* would
>>> simplify things. If there are benchmarks that prove me wrong, I'll
>>> gladly spend some time optimizing that.
>>
>>
>> In the general case, one reason for designing the locking outside of a
>> utilities like this, is that different callers may have
>> different requirements. For example, the call path is known not to be
>> multithreaded at all, or
>> the caller may prefer a mutex over a spinlock for various reasons. It might
>> also be that some callers will want to use
>> RCU locking in the future if the lookup path becomes busy, and that would
>> require *all* users to adapt to RCU object destruction...
>>
>> I haven't looked at the code closely enough to say that any of this applies
>> in this particular case, though.
>
> Some notes why I went with local locking:
>  - mmap offset creation is done once and is independent of any other
> operations you might do on the BO in parallel
>  - mmap lookup is also only done once in most cases. User-space rarely
> maps a buffer twice (setup/teardown of mmaps is expensive, but keeping
> them around is very cheap). And for shared buffers only the writer
> maps it as the reader normally passes it to the kernel without
> mapping/touching it. Only for software-rendering we have two or more
> mappings of the same object.
>  - creating/mapping/destroying buffer objects is expensive in its
> current form and buffers tend to stay around for a long time
>
> I couldn't find a situation were the vma-manager was part of a
> performance critical path. But on the other hand, the paths were it is
> used are quite heavy. That's why I don't want to lock the whole buffer
> or even device. Instead, we need some "management lock" which is used
> for mmap-setup or similar things that don't affect other operations on
> the buffer or device. We don't have such a lock, so I introduced local
> locking. If we end up with more use-cases, I volunteer to refactor
> this. But I am no big fan of overgeneralizing it before more uses are
> known.
>
> Anyway, I will resend with vm_lock removed (+_unlocked() helpers) so
> we keep the status-quo regarding locks. Thanks for the comments on TTM
> buffer transfer.
>
> Thanks
> David

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-22 10:55               ` David Herrmann
@ 2013-07-22 11:45                 ` Thomas Hellstrom
  0 siblings, 0 replies; 31+ messages in thread
From: Thomas Hellstrom @ 2013-07-22 11:45 UTC (permalink / raw)
  To: David Herrmann
  Cc: Martin Peres, dri-devel, Alex Deucher, Ben Skeggs, Dave Airlie

On 07/22/2013 12:55 PM, David Herrmann wrote:
> Sorry, I forgot to CC correctly.
>
> On Mon, Jul 22, 2013 at 12:53 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
>> Hi
>>
>> On Fri, Jul 19, 2013 at 11:13 AM, Thomas Hellstrom
>> <thellstrom@vmware.com> wrote:
>>> On 07/18/2013 10:54 PM, David Herrmann wrote:
>>>> Hi
>>>>
>>>> On Thu, Jul 18, 2013 at 1:24 PM, Thomas Hellstrom <thellstrom@vmware.com>
>>>> wrote:
>>>
>>> ...
>>>
>>>
>>>>> I think that if there are good reasons to keep locking internal, I'm fine
>>>>> with that, (And also, of course, with
>>>>> Daniel's proposal). Currently the add / remove / lookup paths are mostly
>>>>> used by TTM during object creation and
>>>>> destruction.
>>>>>
>>>>> However, if the lookup path is ever used by pread / pwrite, that
>>>>> situation
>>>>> might change and we would then like to
>>>>> minimize the locking.
>>>> I tried to keep the change as minimal as I could. Follow-up patches
>>>> are welcome. I just thought pushing the lock into drm_vma_* would
>>>> simplify things. If there are benchmarks that prove me wrong, I'll
>>>> gladly spend some time optimizing that.
>>>
>>> In the general case, one reason for designing the locking outside of a
>>> utilities like this, is that different callers may have
>>> different requirements. For example, the call path is known not to be
>>> multithreaded at all, or
>>> the caller may prefer a mutex over a spinlock for various reasons. It might
>>> also be that some callers will want to use
>>> RCU locking in the future if the lookup path becomes busy, and that would
>>> require *all* users to adapt to RCU object destruction...
>>>
>>> I haven't looked at the code closely enough to say that any of this applies
>>> in this particular case, though.
>> Some notes why I went with local locking:
>>   - mmap offset creation is done once and is independent of any other
>> operations you might do on the BO in parallel
>>   - mmap lookup is also only done once in most cases. User-space rarely
>> maps a buffer twice (setup/teardown of mmaps is expensive, but keeping
>> them around is very cheap). And for shared buffers only the writer
>> maps it as the reader normally passes it to the kernel without
>> mapping/touching it. Only for software-rendering we have two or more
>> mappings of the same object.
>>   - creating/mapping/destroying buffer objects is expensive in its
>> current form and buffers tend to stay around for a long time
>>
>> I couldn't find a situation were the vma-manager was part of a
>> performance critical path. But on the other hand, the paths were it is
>> used are quite heavy. That's why I don't want to lock the whole buffer
>> or even device. Instead, we need some "management lock" which is used
>> for mmap-setup or similar things that don't affect other operations on
>> the buffer or device. We don't have such a lock, so I introduced local
>> locking. If we end up with more use-cases, I volunteer to refactor
>> this. But I am no big fan of overgeneralizing it before more uses are
>> known.

I think we are discussing two different things here:

1) Having a separate lock for vma management vs
2) building that lock into the vma manager utility.

You're arguing for 1) and I completely agree with you, and although I 
still think one generally should avoid building locks into utilities 
like this (avoid 2),
I'm fine with the current approach.

Thanks,
Thomas

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v4 0/4] Unified VMA Offset Manager
  2013-07-17 18:14 [PATCH v3 0/4] Unified VMA Offset Manager v3 David Herrmann
                   ` (3 preceding siblings ...)
  2013-07-17 18:14 ` [PATCH v3 4/4] drm/vma: provide drm_vma_node_unmap() helper David Herrmann
@ 2013-07-23 12:47 ` David Herrmann
  2013-07-23 12:47   ` [PATCH v4 1/4] drm: add unified vma offset manager David Herrmann
                     ` (3 more replies)
  4 siblings, 4 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-23 12:47 UTC (permalink / raw)
  To: dri-devel; +Cc: Dave Airlie, Daniel Vetter

Hi

This is v4 of the unified VMA offset manager. v3 is available here:
  http://comments.gmane.org/gmane.comp.video.dri.devel/88141

Changes since v3:
 - provide drm_vma_offset_lookup_locked() with locking helpers
 - Remove vm_lock from TTM and use drm_vma_offset_lookup_locked() instead
 - Fix typos and comments
 - Rebase on drm-next

Comments welcome!

Cheers
David

David Herrmann (4):
  drm: add unified vma offset manager
  drm/gem: convert to new unified vma manager
  drm/ttm: convert to unified vma offset manager
  drm/vma: provide drm_vma_node_unmap() helper

 Documentation/DocBook/drm.tmpl             |   6 +
 drivers/gpu/drm/Makefile                   |   2 +-
 drivers/gpu/drm/ast/ast_main.c             |   2 +-
 drivers/gpu/drm/cirrus/cirrus_main.c       |   2 +-
 drivers/gpu/drm/drm_gem.c                  |  89 ++-------
 drivers/gpu/drm/drm_gem_cma_helper.c       |  16 +-
 drivers/gpu/drm/drm_vma_manager.c          | 283 +++++++++++++++++++++++++++++
 drivers/gpu/drm/exynos/exynos_drm_gem.c    |  14 +-
 drivers/gpu/drm/gma500/gem.c               |  15 +-
 drivers/gpu/drm/i915/i915_gem.c            |  14 +-
 drivers/gpu/drm/mgag200/mgag200_main.c     |   2 +-
 drivers/gpu/drm/nouveau/nouveau_display.c  |   2 +-
 drivers/gpu/drm/nouveau/nouveau_gem.c      |   2 +-
 drivers/gpu/drm/omapdrm/omap_gem.c         |  28 ++-
 drivers/gpu/drm/omapdrm/omap_gem_helpers.c |  49 +----
 drivers/gpu/drm/qxl/qxl_object.h           |   2 +-
 drivers/gpu/drm/qxl/qxl_release.c          |   2 +-
 drivers/gpu/drm/radeon/radeon_object.h     |   5 +-
 drivers/gpu/drm/ttm/ttm_bo.c               |  84 +--------
 drivers/gpu/drm/ttm/ttm_bo_util.c          |   3 +-
 drivers/gpu/drm/ttm/ttm_bo_vm.c            |  81 ++++-----
 drivers/gpu/drm/udl/udl_gem.c              |  13 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_resource.c   |   4 +-
 drivers/gpu/host1x/drm/gem.c               |   5 +-
 include/drm/drmP.h                         |   7 +-
 include/drm/drm_vma_manager.h              | 202 ++++++++++++++++++++
 include/drm/ttm/ttm_bo_api.h               |  15 +-
 include/drm/ttm/ttm_bo_driver.h            |  10 +-
 include/uapi/drm/drm.h                     |   2 +-
 29 files changed, 613 insertions(+), 348 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_vma_manager.c
 create mode 100644 include/drm/drm_vma_manager.h

-- 
1.8.3.3

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v4 1/4] drm: add unified vma offset manager
  2013-07-23 12:47 ` [PATCH v4 0/4] Unified VMA Offset Manager David Herrmann
@ 2013-07-23 12:47   ` David Herrmann
  2013-07-24 15:35     ` Daniel Vetter
  2013-07-24 19:06     ` [PATCH v5 " David Herrmann
  2013-07-23 12:47   ` [PATCH v4 2/4] drm/gem: convert to new unified vma manager David Herrmann
                     ` (2 subsequent siblings)
  3 siblings, 2 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-23 12:47 UTC (permalink / raw)
  To: dri-devel; +Cc: Dave Airlie, Daniel Vetter

If we want to map GPU memory into user-space, we need to linearize the
addresses to not confuse mm-core. Currently, GEM and TTM both implement
their own offset-managers to assign a pgoff to each object for user-space
CPU access. GEM uses a hash-table, TTM uses an rbtree.

This patch provides a unified implementation that can be used to replace
both. TTM allows partial mmaps with a given offset, so we cannot use
hashtables as the start address may not be known at mmap time. Hence, we
use the rbtree-implementation of TTM.

We could easily update drm_mm to use an rbtree instead of a linked list
for it's object list and thus drop the rbtree from the vma-manager.
However, this would slow down drm_mm object allocation for all other
use-cases (rbtree insertion) and add another 4-8 bytes to each mm node.
Hence, use the separate tree but allow for later migration.

This is a rewrite of the 2012-proposal by David Airlie <airlied@linux.ie>

v2:
 - fix Docbook integration
 - drop drm_mm_node_linked() and use drm_mm_node_allocated()
 - remove unjustified likely/unlikely usage (but keep for rbtree paths)
 - remove BUG_ON() as drm_mm already does that
 - clarify page-based vs. byte-based addresses
 - use drm_vma_node_reset() for initialization, too
v4:
 - allow external locking via drm_vma_offset_un/lock_lookup()
 - add locked lookup helper drm_vma_offset_lookup_locked()

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 Documentation/DocBook/drm.tmpl    |   6 +
 drivers/gpu/drm/Makefile          |   2 +-
 drivers/gpu/drm/drm_vma_manager.c | 283 ++++++++++++++++++++++++++++++++++++++
 include/drm/drm_vma_manager.h     | 180 ++++++++++++++++++++++++
 4 files changed, 470 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/drm_vma_manager.c
 create mode 100644 include/drm/drm_vma_manager.h

diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl
index 7d1278e..87e22ec 100644
--- a/Documentation/DocBook/drm.tmpl
+++ b/Documentation/DocBook/drm.tmpl
@@ -2212,6 +2212,12 @@ void intel_crt_init(struct drm_device *dev)
 !Iinclude/drm/drm_rect.h
 !Edrivers/gpu/drm/drm_rect.c
     </sect2>
+    <sect2>
+      <title>VMA Offset Manager</title>
+!Pdrivers/gpu/drm/drm_vma_manager.c vma offset manager
+!Edrivers/gpu/drm/drm_vma_manager.c
+!Iinclude/drm/drm_vma_manager.h
+    </sect2>
   </sect1>
 
   <!-- Internals: kms properties -->
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 801bcaf..d943b94 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -13,7 +13,7 @@ drm-y       :=	drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
 		drm_crtc.o drm_modes.o drm_edid.o \
 		drm_info.o drm_debugfs.o drm_encoder_slave.o \
 		drm_trace_points.o drm_global.o drm_prime.o \
-		drm_rect.o
+		drm_rect.o drm_vma_manager.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c
new file mode 100644
index 0000000..27760bd
--- /dev/null
+++ b/drivers/gpu/drm/drm_vma_manager.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
+ * Copyright (c) 2012 David Airlie <airlied@linux.ie>
+ * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_mm.h>
+#include <drm/drm_vma_manager.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+/**
+ * DOC: vma offset manager
+ *
+ * The vma-manager is responsible to map arbitrary driver-dependent memory
+ * regions into the linear user address-space. It provides offsets to the
+ * caller which can then be used on the address_space of the drm-device. It
+ * takes care to not overlap regions, size them appropriately and to not
+ * confuse mm-core by inconsistent fake vm_pgoff fields.
+ * Drivers shouldn't use this for object placement in VMEM. This manager should
+ * only be used to manage mappings into linear user-space VMs.
+ *
+ * We use drm_mm as backend to manage object allocations. But it is highly
+ * optimized for alloc/free calls, not lookups. Hence, we use an rb-tree to
+ * speed up offset lookups.
+ *
+ * You must not use multiple offset managers on a single address_space.
+ * Otherwise, mm-core will be unable to tear down memory mappings as the VM will
+ * no longer be linear. Please use VM_NONLINEAR in that case and implement your
+ * own offset managers.
+ *
+ * This offset manager works on page-based addresses. That is, every argument
+ * and return code (with the exception of drm_vma_node_offset_addr()) is given
+ * in number of pages, not number of bytes. That means, object sizes and offsets
+ * must always be page-aligned (as usual).
+ * If you want to get a valid byte-based user-space address for a given offset,
+ * please see drm_vma_node_offset_addr().
+ */
+
+/**
+ * drm_vma_offset_manager_init - Initialize new offset-manager
+ * @mgr: Manager object
+ * @page_offset: Offset of available memory area (page-based)
+ * @size: Size of available memory area (page-based)
+ *
+ * Initialize a new offset-manager. The offset and area size available for the
+ * manager are given as @page_offset and @size. Both are interpreted as
+ * page-numbers, not bytes.
+ *
+ * Adding/removing nodes from the manager is locked internally and protected
+ * against concurrent access. However, node allocation and destruction is left
+ * for the caller. While calling into the vma-manager, a given node must
+ * always be guaranteed to be referenced.
+ */
+void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
+				 unsigned long page_offset, unsigned long size)
+{
+	rwlock_init(&mgr->vm_lock);
+	mgr->vm_addr_space_rb = RB_ROOT;
+	drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size);
+}
+EXPORT_SYMBOL(drm_vma_offset_manager_init);
+
+/**
+ * drm_vma_offset_manager_destroy() - Destroy offset manager
+ * @mgr: Manager object
+ *
+ * Destroy an object manager which was previously created via
+ * drm_vma_offset_manager_init(). The caller must remove all allocated nodes
+ * before destroying the manager. Otherwise, drm_mm will refuse to free the
+ * requested resources.
+ *
+ * The manager must not be accessed after this function is called.
+ */
+void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr)
+{
+	/* take the lock to protect against buggy drivers */
+	write_lock(&mgr->vm_lock);
+	drm_mm_takedown(&mgr->vm_addr_space_mm);
+	write_unlock(&mgr->vm_lock);
+}
+EXPORT_SYMBOL(drm_vma_offset_manager_destroy);
+
+/**
+ * drm_vma_offset_lookup() - Find node in offset space
+ * @mgr: Manager object
+ * @start: Start address for object (page-based)
+ * @pages: Size of object (page-based)
+ *
+ * Find a node given a start address and object size. This returns the _best_
+ * match for the given node. That is, @start may point somewhere into a valid
+ * region and the given node will be returned, as long as the node spans the
+ * whole requested area (given the size in number of pages as @pages).
+ *
+ * RETURNS:
+ * Returns NULL if no suitable node can be found. Otherwise, the best match
+ * is returned. It's the caller's responsibility to make sure the node doesn't
+ * get destroyed before the caller can access it.
+ */
+struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr,
+						  unsigned long start,
+						  unsigned long pages)
+{
+	struct drm_vma_offset_node *node;
+
+	read_lock(&mgr->vm_lock);
+	node = drm_vma_offset_lookup_locked(mgr, start, pages);
+	read_unlock(&mgr->vm_lock);
+
+	return node;
+}
+EXPORT_SYMBOL(drm_vma_offset_lookup);
+
+/**
+ * drm_vma_offset_lookup_locked() - Find node in offset space
+ * @mgr: Manager object
+ * @start: Start address for object (page-based)
+ * @pages: Size of object (page-based)
+ *
+ * Same as drm_vma_offset_lookup() but requires the caller to lock offset lookup
+ * manually. See drm_vma_offset_lock_lookup() for an example.
+ *
+ * RETURNS:
+ * Returns NULL if no suitable node can be found. Otherwise, the best match
+ * is returned.
+ */
+struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
+							 unsigned long start,
+							 unsigned long pages)
+{
+	struct drm_vma_offset_node *node, *best;
+	struct rb_node *iter;
+	unsigned long offset;
+
+	iter = mgr->vm_addr_space_rb.rb_node;
+	best = NULL;
+
+	while (likely(iter)) {
+		node = rb_entry(iter, struct drm_vma_offset_node, vm_rb);
+		offset = node->vm_node.start;
+		if (start >= offset) {
+			iter = iter->rb_right;
+			best = node;
+			if (start == offset)
+				break;
+		} else {
+			iter = iter->rb_left;
+		}
+	}
+
+	/* verify that the node spans the requested area */
+	if (best) {
+		offset = best->vm_node.start + best->vm_pages;
+		if (offset > start + pages)
+			best = NULL;
+	}
+
+	return best;
+}
+EXPORT_SYMBOL(drm_vma_offset_lookup_locked);
+
+/* internal helper to link @node into the rb-tree */
+static void _drm_vma_offset_add_rb(struct drm_vma_offset_manager *mgr,
+				   struct drm_vma_offset_node *node)
+{
+	struct rb_node **iter = &mgr->vm_addr_space_rb.rb_node;
+	struct rb_node *parent = NULL;
+	struct drm_vma_offset_node *iter_node;
+
+	while (likely(*iter)) {
+		parent = *iter;
+		iter_node = rb_entry(*iter, struct drm_vma_offset_node, vm_rb);
+
+		if (node->vm_node.start < iter_node->vm_node.start)
+			iter = &(*iter)->rb_left;
+		else if (node->vm_node.start > iter_node->vm_node.start)
+			iter = &(*iter)->rb_right;
+		else
+			BUG();
+	}
+
+	rb_link_node(&node->vm_rb, parent, iter);
+	rb_insert_color(&node->vm_rb, &mgr->vm_addr_space_rb);
+}
+
+/**
+ * drm_vma_offset_add() - Add offset node to manager
+ * @mgr: Manager object
+ * @node: Node to be added
+ * @pages: Allocation size visible to user-space (in number of pages)
+ *
+ * Add a node to the offset-manager. If the node was already added, this does
+ * nothing and return 0. @pages is the size of the object given in number of
+ * pages.
+ * After this call succeeds, you can access the offset of the node until it
+ * is removed again.
+ *
+ * If this call fails, it is safe to retry the operation or call
+ * drm_vma_offset_remove(), anyway. However, no cleanup is required in that
+ * case.
+ *
+ * @pages is not required to be the same size as the underlying memory object
+ * that you want to map. It only limits the size that user-space can map into
+ * their address space.
+ *
+ * RETURNS:
+ * 0 on success, negative error code on failure.
+ */
+int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
+		       struct drm_vma_offset_node *node, unsigned long pages)
+{
+	int ret;
+
+	write_lock(&mgr->vm_lock);
+
+	if (drm_mm_node_allocated(&node->vm_node)) {
+		ret = 0;
+		goto out_unlock;
+	}
+
+	ret = drm_mm_insert_node_generic(&mgr->vm_addr_space_mm,
+					 &node->vm_node, pages, 0, 0);
+	if (ret)
+		goto out_unlock;
+
+	node->vm_pages = pages;
+	_drm_vma_offset_add_rb(mgr, node);
+	goto out_unlock;
+
+out_unlock:
+	write_unlock(&mgr->vm_lock);
+	return ret;
+}
+EXPORT_SYMBOL(drm_vma_offset_add);
+
+/**
+ * drm_vma_offset_remove() - Remove offset node from manager
+ * @mgr: Manager object
+ * @node: Node to be removed
+ *
+ * Remove a node from the offset manager. If the node wasn't added before, this
+ * does nothing. After this call returns, the offset of the node must be not
+ * accessed, anymore.
+ * It is safe to add the node via drm_vma_offset_add() again.
+ */
+void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
+			   struct drm_vma_offset_node *node)
+{
+	write_lock(&mgr->vm_lock);
+
+	if (drm_mm_node_allocated(&node->vm_node)) {
+		rb_erase(&node->vm_rb, &mgr->vm_addr_space_rb);
+		drm_mm_remove_node(&node->vm_node);
+		memset(&node->vm_node, 0, sizeof(node->vm_node));
+		node->vm_pages = 0;
+	}
+
+	write_unlock(&mgr->vm_lock);
+}
+EXPORT_SYMBOL(drm_vma_offset_remove);
diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h
new file mode 100644
index 0000000..ace2925
--- /dev/null
+++ b/include/drm/drm_vma_manager.h
@@ -0,0 +1,180 @@
+#ifndef __DRM_VMA_MANAGER_H__
+#define __DRM_VMA_MANAGER_H__
+
+/*
+ * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drm_mm.h>
+#include <linux/module.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+struct drm_vma_offset_node {
+	struct drm_mm_node vm_node;
+	struct rb_node vm_rb;
+	unsigned long vm_pages;
+};
+
+struct drm_vma_offset_manager {
+	rwlock_t vm_lock;
+	struct rb_root vm_addr_space_rb;
+	struct drm_mm vm_addr_space_mm;
+};
+
+void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
+				 unsigned long page_offset, unsigned long size);
+void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr);
+
+struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr,
+						  unsigned long start,
+						  unsigned long pages);
+struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
+							   unsigned long start,
+							   unsigned long pages);
+int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
+		       struct drm_vma_offset_node *node, unsigned long pages);
+void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
+			   struct drm_vma_offset_node *node);
+
+/**
+ * drm_vma_offset_exact_lookup() - Look up node by exact address
+ * @mgr: Manager object
+ * @start: Start address (page-based, not byte-based)
+ *
+ * Same as drm_vma_offset_lookup() but does not allow any offset into the node.
+ * It only returns the exact object with the given start address.
+ *
+ * RETURNS:
+ * Node at exact start address @start.
+ */
+static inline struct drm_vma_offset_node *
+drm_vma_offset_exact_lookup(struct drm_vma_offset_manager *mgr,
+			    unsigned long start)
+{
+	return drm_vma_offset_lookup(mgr, start, 1);
+}
+
+/**
+ * drm_vma_offset_lock_lookup() - Lock lookup for extended private use
+ * @mgr: Manager object
+ *
+ * Lock VMA manager for extended lookups. Only *_locked() VMA function calls
+ * are allowed while holding this lock. All other contexts are blocked from VMA
+ * until the lock is released via drm_vma_offset_unlock_lookup().
+ *
+ * Use this if you need to take a reference to the objects returned by
+ * drm_vma_offset_lookup_locked() before releasing this lock again.
+ *
+ * This lock must not be used for anything else than extended lookups. You must
+ * not call any other VMA helpers while holding this lock.
+ *
+ * Note: You're in atomic-context while holding this lock!
+ *
+ * Example:
+ *   drm_vma_offset_lock_lookup(mgr);
+ *   node = drm_vma_offset_lookup_locked(mgr);
+ *   if (node)
+ *       kref_get_unless_zero(container_of(node, sth, entr));
+ *   drm_vma_offset_unlock_lookup(mgr);
+ */
+static inline void drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr)
+{
+	read_lock(&mgr->vm_lock);
+}
+
+/**
+ * drm_vma_offset_unlock_lookup() - Unlock lookup for extended private use
+ * @mgr: Manager object
+ *
+ * Release lookup-lock. See drm_vma_offset_lock_lookup() for more information.
+ */
+static inline void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr)
+{
+	read_unlock(&mgr->vm_lock);
+}
+
+/**
+ * drm_vma_node_reset() - Initialize or reset node object
+ * @node: Node to initialize or reset
+ *
+ * Reset a node to its initial state. You must call this before using @node with
+ * any vma offset manager.
+ *
+ * This must not be called on an already allocated node, or you will leak
+ * memory.
+ */
+static inline void drm_vma_node_reset(struct drm_vma_offset_node *node)
+{
+	memset(node, 0, sizeof(*node));
+}
+
+/**
+ * drm_vma_node_start() - Return start address for page-based addressing
+ * @node: Node to inspect
+ *
+ * Return the start address of the given node. This can be used as offset into
+ * the linear VM space that is provided by the VMA offset manager. Note that
+ * this can only be used for page-based addressing. If you need a proper offset
+ * for user-space mappings, you must apply "<< PAGE_SHIFT" or use the
+ * drm_vma_node_offset_addr() helper instead.
+ *
+ * RETURNS:
+ * Start address of @node for page-based addressing. 0 if the node does not
+ * have an offset allocated.
+ */
+static inline unsigned long drm_vma_node_start(struct drm_vma_offset_node *node)
+{
+	return node->vm_node.start;
+}
+
+/**
+ * drm_vma_node_has_offset() - Check whether node is added to offset manager
+ * @node: Node to be checked
+ *
+ * RETURNS:
+ * true iff the node was previously allocated an offset and added to
+ * an vma offset manager.
+ */
+static inline bool drm_vma_node_has_offset(struct drm_vma_offset_node *node)
+{
+	return drm_mm_node_allocated(&node->vm_node);
+}
+
+/**
+ * drm_vma_node_offset_addr() - Return sanitized offset for user-space mmaps
+ * @node: Linked offset node
+ *
+ * Same as drm_vma_node_start() but returns the address as a valid offset that
+ * can be used for user-space mappings during mmap().
+ * This must not be called on unlinked nodes.
+ *
+ * RETURNS:
+ * Offset of @node for byte-based addressing. 0 if the node does not have an
+ * object allocated.
+ */
+static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
+{
+	return ((__u64)node->vm_node.start) << PAGE_SHIFT;
+}
+
+#endif /* __DRM_VMA_MANAGER_H__ */
-- 
1.8.3.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v4 2/4] drm/gem: convert to new unified vma manager
  2013-07-23 12:47 ` [PATCH v4 0/4] Unified VMA Offset Manager David Herrmann
  2013-07-23 12:47   ` [PATCH v4 1/4] drm: add unified vma offset manager David Herrmann
@ 2013-07-23 12:47   ` David Herrmann
  2013-07-24 15:52     ` Daniel Vetter
  2013-07-24 19:07     ` [PATCH v5 " David Herrmann
  2013-07-23 12:47   ` [PATCH v4 3/4] drm/ttm: convert to unified vma offset manager David Herrmann
  2013-07-23 12:47   ` [PATCH v4 4/4] drm/vma: provide drm_vma_node_unmap() helper David Herrmann
  3 siblings, 2 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-23 12:47 UTC (permalink / raw)
  To: dri-devel; +Cc: Daniel Vetter, Dave Airlie

Use the new vma manager instead of the old hashtable. Also convert all
drivers to use the new convenience helpers. This drops all the
(map_list.hash.key << PAGE_SHIFT) non-sense.

Locking and access-management is exactly the same as before with an
additional lock inside of the vma-manager, which strictly wouldn't be
needed for gem.

v2:
 - rebase on drm-next
 - init nodes via drm_vma_node_reset() in drm_gem.c
v3:
 - fix tegra
v4:
 - remove duplicate if (drm_vma_node_has_offset()) checks
 - inline now trivial drm_vma_node_offset_addr() calls

Cc: Inki Dae <inki.dae@samsung.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Rob Clark <robdclark@gmail.com>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
---
 drivers/gpu/drm/drm_gem.c                  | 89 +++++-------------------------
 drivers/gpu/drm/drm_gem_cma_helper.c       | 16 ++----
 drivers/gpu/drm/exynos/exynos_drm_gem.c    | 14 ++---
 drivers/gpu/drm/gma500/gem.c               | 15 ++---
 drivers/gpu/drm/i915/i915_gem.c            | 10 ++--
 drivers/gpu/drm/omapdrm/omap_gem.c         | 28 +++++-----
 drivers/gpu/drm/omapdrm/omap_gem_helpers.c | 49 +---------------
 drivers/gpu/drm/udl/udl_gem.c              | 13 ++---
 drivers/gpu/host1x/drm/gem.c               |  5 +-
 include/drm/drmP.h                         |  7 +--
 include/uapi/drm/drm.h                     |  2 +-
 11 files changed, 62 insertions(+), 186 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 1ad9e7e..8d3cdf3 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -37,6 +37,7 @@
 #include <linux/shmem_fs.h>
 #include <linux/dma-buf.h>
 #include <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
 
 /** @file drm_gem.c
  *
@@ -102,14 +103,9 @@ drm_gem_init(struct drm_device *dev)
 	}
 
 	dev->mm_private = mm;
-
-	if (drm_ht_create(&mm->offset_hash, 12)) {
-		kfree(mm);
-		return -ENOMEM;
-	}
-
-	drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START,
-		    DRM_FILE_PAGE_OFFSET_SIZE);
+	drm_vma_offset_manager_init(&mm->vma_manager,
+				    DRM_FILE_PAGE_OFFSET_START,
+				    DRM_FILE_PAGE_OFFSET_SIZE);
 
 	return 0;
 }
@@ -119,8 +115,7 @@ drm_gem_destroy(struct drm_device *dev)
 {
 	struct drm_gem_mm *mm = dev->mm_private;
 
-	drm_mm_takedown(&mm->offset_manager);
-	drm_ht_remove(&mm->offset_hash);
+	drm_vma_offset_manager_destroy(&mm->vma_manager);
 	kfree(mm);
 	dev->mm_private = NULL;
 }
@@ -160,6 +155,7 @@ void drm_gem_private_object_init(struct drm_device *dev,
 
 	kref_init(&obj->refcount);
 	atomic_set(&obj->handle_count, 0);
+	drm_vma_node_reset(&obj->vma_node);
 	obj->size = size;
 }
 EXPORT_SYMBOL(drm_gem_private_object_init);
@@ -302,12 +298,8 @@ drm_gem_free_mmap_offset(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
 	struct drm_gem_mm *mm = dev->mm_private;
-	struct drm_map_list *list = &obj->map_list;
 
-	drm_ht_remove_item(&mm->offset_hash, &list->hash);
-	drm_mm_put_block(list->file_offset_node);
-	kfree(list->map);
-	list->map = NULL;
+	drm_vma_offset_remove(&mm->vma_manager, &obj->vma_node);
 }
 EXPORT_SYMBOL(drm_gem_free_mmap_offset);
 
@@ -327,54 +319,9 @@ drm_gem_create_mmap_offset(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
 	struct drm_gem_mm *mm = dev->mm_private;
-	struct drm_map_list *list;
-	struct drm_local_map *map;
-	int ret;
-
-	/* Set the object up for mmap'ing */
-	list = &obj->map_list;
-	list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
-	if (!list->map)
-		return -ENOMEM;
-
-	map = list->map;
-	map->type = _DRM_GEM;
-	map->size = obj->size;
-	map->handle = obj;
-
-	/* Get a DRM GEM mmap offset allocated... */
-	list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
-			obj->size / PAGE_SIZE, 0, false);
-
-	if (!list->file_offset_node) {
-		DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
-		ret = -ENOSPC;
-		goto out_free_list;
-	}
 
-	list->file_offset_node = drm_mm_get_block(list->file_offset_node,
-			obj->size / PAGE_SIZE, 0);
-	if (!list->file_offset_node) {
-		ret = -ENOMEM;
-		goto out_free_list;
-	}
-
-	list->hash.key = list->file_offset_node->start;
-	ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
-	if (ret) {
-		DRM_ERROR("failed to add to map hash\n");
-		goto out_free_mm;
-	}
-
-	return 0;
-
-out_free_mm:
-	drm_mm_put_block(list->file_offset_node);
-out_free_list:
-	kfree(list->map);
-	list->map = NULL;
-
-	return ret;
+	return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node,
+				  obj->size / PAGE_SIZE);
 }
 EXPORT_SYMBOL(drm_gem_create_mmap_offset);
 
@@ -703,8 +650,8 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
 	struct drm_file *priv = filp->private_data;
 	struct drm_device *dev = priv->minor->dev;
 	struct drm_gem_mm *mm = dev->mm_private;
-	struct drm_local_map *map = NULL;
-	struct drm_hash_item *hash;
+	struct drm_gem_object *obj;
+	struct drm_vma_offset_node *node;
 	int ret = 0;
 
 	if (drm_device_is_unplugged(dev))
@@ -712,21 +659,15 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
 
 	mutex_lock(&dev->struct_mutex);
 
-	if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
+	node = drm_vma_offset_exact_lookup(&mm->vma_manager, vma->vm_pgoff);
+	if (!node) {
 		mutex_unlock(&dev->struct_mutex);
 		return drm_mmap(filp, vma);
 	}
 
-	map = drm_hash_entry(hash, struct drm_map_list, hash)->map;
-	if (!map ||
-	    ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) {
-		ret =  -EPERM;
-		goto out_unlock;
-	}
-
-	ret = drm_gem_mmap_obj(map->handle, map->size, vma);
+	obj = container_of(node, struct drm_gem_object, vma_node);
+	ret = drm_gem_mmap_obj(obj, node->vm_pages, vma);
 
-out_unlock:
 	mutex_unlock(&dev->struct_mutex);
 
 	return ret;
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
index ece72a8..bf09a6dc 100644
--- a/drivers/gpu/drm/drm_gem_cma_helper.c
+++ b/drivers/gpu/drm/drm_gem_cma_helper.c
@@ -27,11 +27,7 @@
 #include <drm/drmP.h>
 #include <drm/drm.h>
 #include <drm/drm_gem_cma_helper.h>
-
-static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
-{
-	return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
-}
+#include <drm/drm_vma_manager.h>
 
 /*
  * __drm_gem_cma_create - Create a GEM CMA object without allocating memory
@@ -172,8 +168,7 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
 {
 	struct drm_gem_cma_object *cma_obj;
 
-	if (gem_obj->map_list.map)
-		drm_gem_free_mmap_offset(gem_obj);
+	drm_gem_free_mmap_offset(gem_obj);
 
 	cma_obj = to_drm_gem_cma_obj(gem_obj);
 
@@ -237,7 +232,7 @@ int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
 		return -EINVAL;
 	}
 
-	*offset = get_gem_mmap_offset(gem_obj);
+	*offset = (unsigned int)drm_vma_node_offset_addr(&gem_obj->vma_node);
 
 	drm_gem_object_unreference(gem_obj);
 
@@ -301,12 +296,11 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m
 {
 	struct drm_gem_object *obj = &cma_obj->base;
 	struct drm_device *dev = obj->dev;
-	uint64_t off = 0;
+	uint64_t off;
 
 	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
-	if (obj->map_list.map)
-		off = (uint64_t)obj->map_list.hash.key;
+	off = drm_vma_node_start(&obj->vma_node);
 
 	seq_printf(m, "%2d (%2d) %08llx %08Zx %p %d",
 			obj->name, obj->refcount.refcount.counter,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index 24c22a8..be32db1 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -10,6 +10,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
 
 #include <linux/shmem_fs.h>
 #include <drm/exynos_drm.h>
@@ -152,8 +153,7 @@ out:
 	exynos_drm_fini_buf(obj->dev, buf);
 	exynos_gem_obj->buffer = NULL;
 
-	if (obj->map_list.map)
-		drm_gem_free_mmap_offset(obj);
+	drm_gem_free_mmap_offset(obj);
 
 	/* release file pointer to gem object. */
 	drm_gem_object_release(obj);
@@ -703,13 +703,11 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
 		goto unlock;
 	}
 
-	if (!obj->map_list.map) {
-		ret = drm_gem_create_mmap_offset(obj);
-		if (ret)
-			goto out;
-	}
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret)
+		goto out;
 
-	*offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
+	*offset = drm_vma_node_offset_addr(&obj->vma_node);
 	DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
 
 out:
diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c
index fe1d332..2f77bea 100644
--- a/drivers/gpu/drm/gma500/gem.c
+++ b/drivers/gpu/drm/gma500/gem.c
@@ -26,6 +26,7 @@
 #include <drm/drmP.h>
 #include <drm/drm.h>
 #include <drm/gma_drm.h>
+#include <drm/drm_vma_manager.h>
 #include "psb_drv.h"
 
 int psb_gem_init_object(struct drm_gem_object *obj)
@@ -38,8 +39,7 @@ void psb_gem_free_object(struct drm_gem_object *obj)
 	struct gtt_range *gtt = container_of(obj, struct gtt_range, gem);
 
 	/* Remove the list map if one is present */
-	if (obj->map_list.map)
-		drm_gem_free_mmap_offset(obj);
+	drm_gem_free_mmap_offset(obj);
 	drm_gem_object_release(obj);
 
 	/* This must occur last as it frees up the memory of the GEM object */
@@ -81,13 +81,10 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev,
 	/* What validation is needed here ? */
 
 	/* Make it mmapable */
-	if (!obj->map_list.map) {
-		ret = drm_gem_create_mmap_offset(obj);
-		if (ret)
-			goto out;
-	}
-	/* GEM should really work out the hash offsets for us */
-	*offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret)
+		goto out;
+	*offset = drm_vma_node_offset_addr(&obj->vma_node);
 out:
 	drm_gem_object_unreference(obj);
 unlock:
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 46bf7e3..53f81b3 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -26,6 +26,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 #include "i915_trace.h"
@@ -1428,7 +1429,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
 
 	if (obj->base.dev->dev_mapping)
 		unmap_mapping_range(obj->base.dev->dev_mapping,
-				    (loff_t)obj->base.map_list.hash.key<<PAGE_SHIFT,
+				    (loff_t)drm_vma_node_offset_addr(&obj->base.vma_node),
 				    obj->base.size, 1);
 
 	obj->fault_mappable = false;
@@ -1486,7 +1487,7 @@ static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj)
 	struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
 	int ret;
 
-	if (obj->base.map_list.map)
+	if (drm_vma_node_has_offset(&obj->base.vma_node))
 		return 0;
 
 	dev_priv->mm.shrinker_no_lock_stealing = true;
@@ -1517,9 +1518,6 @@ out:
 
 static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj)
 {
-	if (!obj->base.map_list.map)
-		return;
-
 	drm_gem_free_mmap_offset(&obj->base);
 }
 
@@ -1558,7 +1556,7 @@ i915_gem_mmap_gtt(struct drm_file *file,
 	if (ret)
 		goto out;
 
-	*offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT;
+	*offset = drm_vma_node_offset_addr(&obj->base.vma_node);
 
 out:
 	drm_gem_object_unreference(&obj->base);
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
index cbcd71e..f90531fc00 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -20,6 +20,7 @@
 
 #include <linux/spinlock.h>
 #include <linux/shmem_fs.h>
+#include <drm/drm_vma_manager.h>
 
 #include "omap_drv.h"
 #include "omap_dmm_tiler.h"
@@ -308,21 +309,20 @@ uint32_t omap_gem_flags(struct drm_gem_object *obj)
 static uint64_t mmap_offset(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
+	int ret;
+	size_t size;
 
 	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
-	if (!obj->map_list.map) {
-		/* Make it mmapable */
-		size_t size = omap_gem_mmap_size(obj);
-		int ret = _drm_gem_create_mmap_offset_size(obj, size);
-
-		if (ret) {
-			dev_err(dev->dev, "could not allocate mmap offset\n");
-			return 0;
-		}
+	/* Make it mmapable */
+	size = omap_gem_mmap_size(obj);
+	ret = _drm_gem_create_mmap_offset_size(obj, size);
+	if (ret) {
+		dev_err(dev->dev, "could not allocate mmap offset\n");
+		return 0;
 	}
 
-	return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT;
+	return drm_vma_node_offset_addr(&obj->vma_node);
 }
 
 uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
@@ -997,12 +997,11 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
 {
 	struct drm_device *dev = obj->dev;
 	struct omap_gem_object *omap_obj = to_omap_bo(obj);
-	uint64_t off = 0;
+	uint64_t off;
 
 	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
-	if (obj->map_list.map)
-		off = (uint64_t)obj->map_list.hash.key;
+	off = drm_vma_node_start(&obj->vma_node);
 
 	seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d",
 			omap_obj->flags, obj->name, obj->refcount.refcount.counter,
@@ -1309,8 +1308,7 @@ void omap_gem_free_object(struct drm_gem_object *obj)
 
 	list_del(&omap_obj->mm_list);
 
-	if (obj->map_list.map)
-		drm_gem_free_mmap_offset(obj);
+	drm_gem_free_mmap_offset(obj);
 
 	/* this means the object is still pinned.. which really should
 	 * not happen.  I think..
diff --git a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
index f9eb679..dbb1575 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
@@ -118,52 +118,7 @@ _drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size)
 {
 	struct drm_device *dev = obj->dev;
 	struct drm_gem_mm *mm = dev->mm_private;
-	struct drm_map_list *list;
-	struct drm_local_map *map;
-	int ret = 0;
-
-	/* Set the object up for mmap'ing */
-	list = &obj->map_list;
-	list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
-	if (!list->map)
-		return -ENOMEM;
-
-	map = list->map;
-	map->type = _DRM_GEM;
-	map->size = size;
-	map->handle = obj;
-
-	/* Get a DRM GEM mmap offset allocated... */
-	list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
-			size / PAGE_SIZE, 0, 0);
-
-	if (!list->file_offset_node) {
-		DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
-		ret = -ENOSPC;
-		goto out_free_list;
-	}
-
-	list->file_offset_node = drm_mm_get_block(list->file_offset_node,
-			size / PAGE_SIZE, 0);
-	if (!list->file_offset_node) {
-		ret = -ENOMEM;
-		goto out_free_list;
-	}
-
-	list->hash.key = list->file_offset_node->start;
-	ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
-	if (ret) {
-		DRM_ERROR("failed to add to map hash\n");
-		goto out_free_mm;
-	}
-
-	return 0;
-
-out_free_mm:
-	drm_mm_put_block(list->file_offset_node);
-out_free_list:
-	kfree(list->map);
-	list->map = NULL;
 
-	return ret;
+	return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node,
+				  size / PAGE_SIZE);
 }
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
index ef034fa..2a4cb2f 100644
--- a/drivers/gpu/drm/udl/udl_gem.c
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -223,8 +223,7 @@ void udl_gem_free_object(struct drm_gem_object *gem_obj)
 	if (obj->pages)
 		udl_gem_put_pages(obj);
 
-	if (gem_obj->map_list.map)
-		drm_gem_free_mmap_offset(gem_obj);
+	drm_gem_free_mmap_offset(gem_obj);
 }
 
 /* the dumb interface doesn't work with the GEM straight MMAP
@@ -247,13 +246,11 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev,
 	ret = udl_gem_get_pages(gobj, GFP_KERNEL);
 	if (ret)
 		goto out;
-	if (!gobj->base.map_list.map) {
-		ret = drm_gem_create_mmap_offset(obj);
-		if (ret)
-			goto out;
-	}
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret)
+		goto out;
 
-	*offset = (u64)gobj->base.map_list.hash.key << PAGE_SHIFT;
+	*offset = drm_vma_node_offset_addr(&gobj->base.vma_node);
 
 out:
 	drm_gem_object_unreference(&gobj->base);
diff --git a/drivers/gpu/host1x/drm/gem.c b/drivers/gpu/host1x/drm/gem.c
index c5e9a9b..bc323b3 100644
--- a/drivers/gpu/host1x/drm/gem.c
+++ b/drivers/gpu/host1x/drm/gem.c
@@ -108,7 +108,7 @@ static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo)
 
 unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo)
 {
-	return (unsigned int)bo->gem.map_list.hash.key << PAGE_SHIFT;
+	return (unsigned int)drm_vma_node_offset_addr(&bo->gem.vma_node);
 }
 
 struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
@@ -182,8 +182,7 @@ void tegra_bo_free_object(struct drm_gem_object *gem)
 {
 	struct tegra_bo *bo = to_tegra_bo(gem);
 
-	if (gem->map_list.map)
-		drm_gem_free_mmap_offset(gem);
+	drm_gem_free_mmap_offset(gem);
 
 	drm_gem_object_release(gem);
 	tegra_bo_destroy(gem->dev, bo);
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 0ab6a09..4b518e0 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -71,6 +71,7 @@
 #include <asm/pgalloc.h>
 #include <drm/drm.h>
 #include <drm/drm_sarea.h>
+#include <drm/drm_vma_manager.h>
 
 #include <linux/idr.h>
 
@@ -587,7 +588,6 @@ struct drm_map_list {
 	struct drm_local_map *map;	/**< mapping */
 	uint64_t user_token;
 	struct drm_master *master;
-	struct drm_mm_node *file_offset_node;	/**< fake offset */
 };
 
 /**
@@ -622,8 +622,7 @@ struct drm_ati_pcigart_info {
  * GEM specific mm private for tracking GEM objects
  */
 struct drm_gem_mm {
-	struct drm_mm offset_manager;	/**< Offset mgmt for buffer objects */
-	struct drm_open_hash offset_hash; /**< User token hash table for maps */
+	struct drm_vma_offset_manager vma_manager;
 };
 
 /**
@@ -644,7 +643,7 @@ struct drm_gem_object {
 	struct file *filp;
 
 	/* Mapping info for this object */
-	struct drm_map_list map_list;
+	struct drm_vma_offset_node vma_node;
 
 	/**
 	 * Size of the object, in bytes.  Immutable over the object's
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index 238a166..272580c 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -181,7 +181,7 @@ enum drm_map_type {
 	_DRM_AGP = 3,		  /**< AGP/GART */
 	_DRM_SCATTER_GATHER = 4,  /**< Scatter/gather memory for PCI DMA */
 	_DRM_CONSISTENT = 5,	  /**< Consistent memory for PCI DMA */
-	_DRM_GEM = 6,		  /**< GEM object */
+	_DRM_GEM = 6,		  /**< GEM object (obsolete) */
 };
 
 /**
-- 
1.8.3.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v4 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-23 12:47 ` [PATCH v4 0/4] Unified VMA Offset Manager David Herrmann
  2013-07-23 12:47   ` [PATCH v4 1/4] drm: add unified vma offset manager David Herrmann
  2013-07-23 12:47   ` [PATCH v4 2/4] drm/gem: convert to new unified vma manager David Herrmann
@ 2013-07-23 12:47   ` David Herrmann
  2013-07-24 15:57     ` Daniel Vetter
  2013-07-24 19:08     ` [PATCH v5 " David Herrmann
  2013-07-23 12:47   ` [PATCH v4 4/4] drm/vma: provide drm_vma_node_unmap() helper David Herrmann
  3 siblings, 2 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-23 12:47 UTC (permalink / raw)
  To: dri-devel
  Cc: Thomas Hellstrom, Martin Peres, Daniel Vetter, Alex Deucher,
	Ben Skeggs, Dave Airlie

Use the new vma-manager infrastructure. This doesn't change any
implementation details as the vma-offset-manager is nearly copied 1-to-1
from TTM.

The vm_lock is moved into the offset manager so we can drop it from TTM.
During lookup, we use the vma locking helpers to take a reference to the
found object.
In all other scenarios, locking stays the same as before. We always
guarantee that drm_vma_offset_remove() is called only during destruction.
Hence, helpers like drm_vma_node_offset_addr() are always safe as long as
the node has a valid offset.

This also drops the addr_space_offset member as it is a copy of vm_start
in vma_node objects. Use the accessor functions instead.

v4:
 - remove vm_lock
 - use drm_vma_offset_lock_lookup() to protect lookup (instead of vm_lock)

Cc: Dave Airlie <airlied@redhat.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Cc: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Cc: Martin Peres <martin.peres@labri.fr>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/gpu/drm/ast/ast_main.c            |  2 +-
 drivers/gpu/drm/cirrus/cirrus_main.c      |  2 +-
 drivers/gpu/drm/mgag200/mgag200_main.c    |  2 +-
 drivers/gpu/drm/nouveau/nouveau_display.c |  2 +-
 drivers/gpu/drm/nouveau/nouveau_gem.c     |  2 +-
 drivers/gpu/drm/qxl/qxl_object.h          |  2 +-
 drivers/gpu/drm/qxl/qxl_release.c         |  2 +-
 drivers/gpu/drm/radeon/radeon_object.h    |  5 +-
 drivers/gpu/drm/ttm/ttm_bo.c              | 89 ++++++-------------------------
 drivers/gpu/drm/ttm/ttm_bo_util.c         |  3 +-
 drivers/gpu/drm/ttm/ttm_bo_vm.c           | 81 +++++++++++-----------------
 drivers/gpu/drm/vmwgfx/vmwgfx_resource.c  |  4 +-
 include/drm/ttm/ttm_bo_api.h              | 15 ++----
 include/drm/ttm/ttm_bo_driver.h           | 10 ++--
 14 files changed, 66 insertions(+), 155 deletions(-)

diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
index f60fd7b..c195dc2 100644
--- a/drivers/gpu/drm/ast/ast_main.c
+++ b/drivers/gpu/drm/ast/ast_main.c
@@ -487,7 +487,7 @@ void ast_gem_free_object(struct drm_gem_object *obj)
 
 static inline u64 ast_bo_mmap_offset(struct ast_bo *bo)
 {
-	return bo->bo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->bo.vma_node);
 }
 int
 ast_dumb_mmap_offset(struct drm_file *file,
diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c
index 35cbae8..3a7a0ef 100644
--- a/drivers/gpu/drm/cirrus/cirrus_main.c
+++ b/drivers/gpu/drm/cirrus/cirrus_main.c
@@ -294,7 +294,7 @@ void cirrus_gem_free_object(struct drm_gem_object *obj)
 
 static inline u64 cirrus_bo_mmap_offset(struct cirrus_bo *bo)
 {
-	return bo->bo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->bo.vma_node);
 }
 
 int
diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c
index 9fa5685..1a75ea3 100644
--- a/drivers/gpu/drm/mgag200/mgag200_main.c
+++ b/drivers/gpu/drm/mgag200/mgag200_main.c
@@ -349,7 +349,7 @@ void mgag200_gem_free_object(struct drm_gem_object *obj)
 
 static inline u64 mgag200_bo_mmap_offset(struct mgag200_bo *bo)
 {
-	return bo->bo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->bo.vma_node);
 }
 
 int
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 708b2d1..7a8caa1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -697,7 +697,7 @@ nouveau_display_dumb_map_offset(struct drm_file *file_priv,
 	gem = drm_gem_object_lookup(dev, file_priv, handle);
 	if (gem) {
 		struct nouveau_bo *bo = gem->driver_private;
-		*poffset = bo->bo.addr_space_offset;
+		*poffset = drm_vma_node_offset_addr(&bo->bo.vma_node);
 		drm_gem_object_unreference_unlocked(gem);
 		return 0;
 	}
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index e72d09c..86597eb 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -226,7 +226,7 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem,
 	}
 
 	rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
-	rep->map_handle = nvbo->bo.addr_space_offset;
+	rep->map_handle = drm_vma_node_offset_addr(&nvbo->bo.vma_node);
 	rep->tile_mode = nvbo->tile_mode;
 	rep->tile_flags = nvbo->tile_flags;
 	return 0;
diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h
index ee7ad79..af10165 100644
--- a/drivers/gpu/drm/qxl/qxl_object.h
+++ b/drivers/gpu/drm/qxl/qxl_object.h
@@ -59,7 +59,7 @@ static inline unsigned long qxl_bo_size(struct qxl_bo *bo)
 
 static inline u64 qxl_bo_mmap_offset(struct qxl_bo *bo)
 {
-	return bo->tbo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->tbo.vma_node);
 }
 
 static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type,
diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c
index b443d67..1a648e1 100644
--- a/drivers/gpu/drm/qxl/qxl_release.c
+++ b/drivers/gpu/drm/qxl/qxl_release.c
@@ -87,7 +87,7 @@ qxl_release_free(struct qxl_device *qdev,
 
 	for (i = 0 ; i < release->bo_count; ++i) {
 		QXL_INFO(qdev, "release %llx\n",
-			release->bos[i]->tbo.addr_space_offset
+			drm_vma_node_offset_addr(&release->bos[i]->tbo.vma_node)
 						- DRM_FILE_OFFSET);
 		qxl_fence_remove_release(&release->bos[i]->fence, release->id);
 		qxl_bo_unref(&release->bos[i]);
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index 49c82c4..209b111 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -113,13 +113,10 @@ static inline unsigned radeon_bo_gpu_page_alignment(struct radeon_bo *bo)
  * @bo:	radeon object for which we query the offset
  *
  * Returns mmap offset of the object.
- *
- * Note: addr_space_offset is constant after ttm bo init thus isn't protected
- * by any lock.
  */
 static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo)
 {
-	return bo->tbo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->tbo.vma_node);
 }
 
 extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index cb9dd67..3dc08b6 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -615,13 +615,7 @@ static void ttm_bo_release(struct kref *kref)
 	struct ttm_bo_device *bdev = bo->bdev;
 	struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type];
 
-	write_lock(&bdev->vm_lock);
-	if (likely(bo->vm_node != NULL)) {
-		rb_erase(&bo->vm_rb, &bdev->addr_space_rb);
-		drm_mm_put_block(bo->vm_node);
-		bo->vm_node = NULL;
-	}
-	write_unlock(&bdev->vm_lock);
+	drm_vma_offset_remove(&bdev->vma_manager, &bo->vma_node);
 	ttm_mem_io_lock(man, false);
 	ttm_mem_io_free_vm(bo);
 	ttm_mem_io_unlock(man);
@@ -1129,6 +1123,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
 	bo->resv = &bo->ttm_resv;
 	reservation_object_init(bo->resv);
 	atomic_inc(&bo->glob->bo_count);
+	drm_vma_node_reset(&bo->vma_node);
 
 	ret = ttm_bo_check_placement(bo, placement);
 
@@ -1424,10 +1419,7 @@ int ttm_bo_device_release(struct ttm_bo_device *bdev)
 		TTM_DEBUG("Swap list was clean\n");
 	spin_unlock(&glob->lru_lock);
 
-	BUG_ON(!drm_mm_clean(&bdev->addr_space_mm));
-	write_lock(&bdev->vm_lock);
-	drm_mm_takedown(&bdev->addr_space_mm);
-	write_unlock(&bdev->vm_lock);
+	drm_vma_offset_manager_destroy(&bdev->vma_manager);
 
 	return ret;
 }
@@ -1441,7 +1433,6 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
 {
 	int ret = -EINVAL;
 
-	rwlock_init(&bdev->vm_lock);
 	bdev->driver = driver;
 
 	memset(bdev->man, 0, sizeof(bdev->man));
@@ -1454,9 +1445,8 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
 	if (unlikely(ret != 0))
 		goto out_no_sys;
 
-	bdev->addr_space_rb = RB_ROOT;
-	drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000);
-
+	drm_vma_offset_manager_init(&bdev->vma_manager, file_page_offset,
+				    0x10000000);
 	INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue);
 	INIT_LIST_HEAD(&bdev->ddestroy);
 	bdev->dev_mapping = NULL;
@@ -1498,12 +1488,17 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
 void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo)
 {
 	struct ttm_bo_device *bdev = bo->bdev;
-	loff_t offset = (loff_t) bo->addr_space_offset;
-	loff_t holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
+	loff_t offset, holelen;
 
 	if (!bdev->dev_mapping)
 		return;
-	unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
+
+	if (drm_vma_node_has_offset(&bo->vma_node)) {
+		offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node);
+		holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
+
+		unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
+	}
 	ttm_mem_io_free_vm(bo);
 }
 
@@ -1520,31 +1515,6 @@ void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo)
 
 EXPORT_SYMBOL(ttm_bo_unmap_virtual);
 
-static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo)
-{
-	struct ttm_bo_device *bdev = bo->bdev;
-	struct rb_node **cur = &bdev->addr_space_rb.rb_node;
-	struct rb_node *parent = NULL;
-	struct ttm_buffer_object *cur_bo;
-	unsigned long offset = bo->vm_node->start;
-	unsigned long cur_offset;
-
-	while (*cur) {
-		parent = *cur;
-		cur_bo = rb_entry(parent, struct ttm_buffer_object, vm_rb);
-		cur_offset = cur_bo->vm_node->start;
-		if (offset < cur_offset)
-			cur = &parent->rb_left;
-		else if (offset > cur_offset)
-			cur = &parent->rb_right;
-		else
-			BUG();
-	}
-
-	rb_link_node(&bo->vm_rb, parent, cur);
-	rb_insert_color(&bo->vm_rb, &bdev->addr_space_rb);
-}
-
 /**
  * ttm_bo_setup_vm:
  *
@@ -1559,38 +1529,9 @@ static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo)
 static int ttm_bo_setup_vm(struct ttm_buffer_object *bo)
 {
 	struct ttm_bo_device *bdev = bo->bdev;
-	int ret;
-
-retry_pre_get:
-	ret = drm_mm_pre_get(&bdev->addr_space_mm);
-	if (unlikely(ret != 0))
-		return ret;
-
-	write_lock(&bdev->vm_lock);
-	bo->vm_node = drm_mm_search_free(&bdev->addr_space_mm,
-					 bo->mem.num_pages, 0, 0);
-
-	if (unlikely(bo->vm_node == NULL)) {
-		ret = -ENOMEM;
-		goto out_unlock;
-	}
 
-	bo->vm_node = drm_mm_get_block_atomic(bo->vm_node,
-					      bo->mem.num_pages, 0);
-
-	if (unlikely(bo->vm_node == NULL)) {
-		write_unlock(&bdev->vm_lock);
-		goto retry_pre_get;
-	}
-
-	ttm_bo_vm_insert_rb(bo);
-	write_unlock(&bdev->vm_lock);
-	bo->addr_space_offset = ((uint64_t) bo->vm_node->start) << PAGE_SHIFT;
-
-	return 0;
-out_unlock:
-	write_unlock(&bdev->vm_lock);
-	return ret;
+	return drm_vma_offset_add(&bdev->vma_manager, &bo->vma_node,
+				  bo->mem.num_pages);
 }
 
 int ttm_bo_wait(struct ttm_buffer_object *bo,
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 319cf41..7cc904d 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -30,6 +30,7 @@
 
 #include <drm/ttm/ttm_bo_driver.h>
 #include <drm/ttm/ttm_placement.h>
+#include <drm/drm_vma_manager.h>
 #include <linux/io.h>
 #include <linux/highmem.h>
 #include <linux/wait.h>
@@ -450,7 +451,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
 	INIT_LIST_HEAD(&fbo->lru);
 	INIT_LIST_HEAD(&fbo->swap);
 	INIT_LIST_HEAD(&fbo->io_reserve_lru);
-	fbo->vm_node = NULL;
+	drm_vma_node_reset(&fbo->vma_node);
 	atomic_set(&fbo->cpu_writers, 0);
 
 	spin_lock(&bdev->fence_lock);
diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
index 3df9f16..8c0e2c0 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
@@ -33,6 +33,7 @@
 #include <ttm/ttm_module.h>
 #include <ttm/ttm_bo_driver.h>
 #include <ttm/ttm_placement.h>
+#include <drm/drm_vma_manager.h>
 #include <linux/mm.h>
 #include <linux/rbtree.h>
 #include <linux/module.h>
@@ -40,37 +41,6 @@
 
 #define TTM_BO_VM_NUM_PREFAULT 16
 
-static struct ttm_buffer_object *ttm_bo_vm_lookup_rb(struct ttm_bo_device *bdev,
-						     unsigned long page_start,
-						     unsigned long num_pages)
-{
-	struct rb_node *cur = bdev->addr_space_rb.rb_node;
-	unsigned long cur_offset;
-	struct ttm_buffer_object *bo;
-	struct ttm_buffer_object *best_bo = NULL;
-
-	while (likely(cur != NULL)) {
-		bo = rb_entry(cur, struct ttm_buffer_object, vm_rb);
-		cur_offset = bo->vm_node->start;
-		if (page_start >= cur_offset) {
-			cur = cur->rb_right;
-			best_bo = bo;
-			if (page_start == cur_offset)
-				break;
-		} else
-			cur = cur->rb_left;
-	}
-
-	if (unlikely(best_bo == NULL))
-		return NULL;
-
-	if (unlikely((best_bo->vm_node->start + best_bo->num_pages) <
-		     (page_start + num_pages)))
-		return NULL;
-
-	return best_bo;
-}
-
 static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
 	struct ttm_buffer_object *bo = (struct ttm_buffer_object *)
@@ -146,9 +116,9 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 	}
 
 	page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) +
-	    bo->vm_node->start - vma->vm_pgoff;
+	    drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
 	page_last = vma_pages(vma) +
-	    bo->vm_node->start - vma->vm_pgoff;
+	    drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
 
 	if (unlikely(page_offset >= bo->num_pages)) {
 		retval = VM_FAULT_SIGBUS;
@@ -249,6 +219,30 @@ static const struct vm_operations_struct ttm_bo_vm_ops = {
 	.close = ttm_bo_vm_close
 };
 
+static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device *bdev,
+						  unsigned long offset,
+						  unsigned long pages)
+{
+	struct drm_vma_offset_node *node;
+	struct ttm_buffer_object *bo = NULL;
+
+	drm_vma_offset_lock_lookup(&bdev->vma_manager);
+
+	node = drm_vma_offset_lookup_locked(&bdev->vma_manager, offset, pages);
+	if (likely(node)) {
+		bo = container_of(node, struct ttm_buffer_object, vma_node);
+		if (!kref_get_unless_zero(&bo->kref))
+			bo = NULL;
+	}
+
+	drm_vma_offset_unlock_lookup(&bdev->vma_manager);
+
+	if (!bo)
+		pr_err("Could not find buffer object to map\n");
+
+	return bo;
+}
+
 int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
 		struct ttm_bo_device *bdev)
 {
@@ -256,17 +250,9 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
 	struct ttm_buffer_object *bo;
 	int ret;
 
-	read_lock(&bdev->vm_lock);
-	bo = ttm_bo_vm_lookup_rb(bdev, vma->vm_pgoff,
-				 vma_pages(vma));
-	if (likely(bo != NULL) && !kref_get_unless_zero(&bo->kref))
-		bo = NULL;
-	read_unlock(&bdev->vm_lock);
-
-	if (unlikely(bo == NULL)) {
-		pr_err("Could not find buffer object to map\n");
+	bo = ttm_bo_vm_lookup(bdev, vma->vm_pgoff, vma_pages(vma));
+	if (unlikely(!bo))
 		return -EINVAL;
-	}
 
 	driver = bo->bdev->driver;
 	if (unlikely(!driver->verify_access)) {
@@ -324,12 +310,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
 	bool no_wait = false;
 	bool dummy;
 
-	read_lock(&bdev->vm_lock);
-	bo = ttm_bo_vm_lookup_rb(bdev, dev_offset, 1);
-	if (likely(bo != NULL))
-		ttm_bo_reference(bo);
-	read_unlock(&bdev->vm_lock);
-
+	bo = ttm_bo_vm_lookup(bdev, dev_offset, 1);
 	if (unlikely(bo == NULL))
 		return -EFAULT;
 
@@ -343,7 +324,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
 	if (unlikely(ret != 0))
 		goto out_unref;
 
-	kmap_offset = dev_offset - bo->vm_node->start;
+	kmap_offset = dev_offset - drm_vma_node_start(&bo->vma_node);
 	if (unlikely(kmap_offset >= bo->num_pages)) {
 		ret = -EFBIG;
 		goto out_unref;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 7953d1f..0e67cf4 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -500,7 +500,7 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
 		goto out_no_dmabuf;
 
 	rep->handle = handle;
-	rep->map_handle = dma_buf->base.addr_space_offset;
+	rep->map_handle = drm_vma_node_offset_addr(&dma_buf->base.vma_node);
 	rep->cur_gmr_id = handle;
 	rep->cur_gmr_offset = 0;
 
@@ -834,7 +834,7 @@ int vmw_dumb_map_offset(struct drm_file *file_priv,
 	if (ret != 0)
 		return -EINVAL;
 
-	*offset = out_buf->base.addr_space_offset;
+	*offset = drm_vma_node_offset_addr(&out_buf->base.vma_node);
 	vmw_dmabuf_unreference(&out_buf);
 	return 0;
 }
diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
index 8a6aa56..751eaff 100644
--- a/include/drm/ttm/ttm_bo_api.h
+++ b/include/drm/ttm/ttm_bo_api.h
@@ -32,12 +32,12 @@
 #define _TTM_BO_API_H_
 
 #include <drm/drm_hashtab.h>
+#include <drm/drm_vma_manager.h>
 #include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/wait.h>
 #include <linux/mutex.h>
 #include <linux/mm.h>
-#include <linux/rbtree.h>
 #include <linux/bitmap.h>
 #include <linux/reservation.h>
 
@@ -145,7 +145,6 @@ struct ttm_tt;
  * @type: The bo type.
  * @destroy: Destruction function. If NULL, kfree is used.
  * @num_pages: Actual number of pages.
- * @addr_space_offset: Address space offset.
  * @acc_size: Accounted size for this object.
  * @kref: Reference count of this buffer object. When this refcount reaches
  * zero, the object is put on the delayed delete list.
@@ -166,8 +165,7 @@ struct ttm_tt;
  * @swap: List head for swap LRU list.
  * @sync_obj: Pointer to a synchronization object.
  * @priv_flags: Flags describing buffer object internal state.
- * @vm_rb: Rb node for the vm rb tree.
- * @vm_node: Address space manager node.
+ * @vma_node: Address space manager node.
  * @offset: The current GPU offset, which can have different meanings
  * depending on the memory type. For SYSTEM type memory, it should be 0.
  * @cur_placement: Hint of current placement.
@@ -194,7 +192,6 @@ struct ttm_buffer_object {
 	enum ttm_bo_type type;
 	void (*destroy) (struct ttm_buffer_object *);
 	unsigned long num_pages;
-	uint64_t addr_space_offset;
 	size_t acc_size;
 
 	/**
@@ -238,13 +235,7 @@ struct ttm_buffer_object {
 	void *sync_obj;
 	unsigned long priv_flags;
 
-	/**
-	 * Members protected by the bdev::vm_lock
-	 */
-
-	struct rb_node vm_rb;
-	struct drm_mm_node *vm_node;
-
+	struct drm_vma_offset_node vma_node;
 
 	/**
 	 * Special members that are protected by the reserve lock
diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
index 984fc2d..8639c85 100644
--- a/include/drm/ttm/ttm_bo_driver.h
+++ b/include/drm/ttm/ttm_bo_driver.h
@@ -36,6 +36,7 @@
 #include <ttm/ttm_placement.h>
 #include <drm/drm_mm.h>
 #include <drm/drm_global.h>
+#include <drm/drm_vma_manager.h>
 #include <linux/workqueue.h>
 #include <linux/fs.h>
 #include <linux/spinlock.h>
@@ -519,7 +520,7 @@ struct ttm_bo_global {
  * @man: An array of mem_type_managers.
  * @fence_lock: Protects the synchronizing members on *all* bos belonging
  * to this device.
- * @addr_space_mm: Range manager for the device address space.
+ * @vma_manager: Address space manager
  * lru_lock: Spinlock that protects the buffer+device lru lists and
  * ddestroy lists.
  * @val_seq: Current validation sequence.
@@ -537,14 +538,13 @@ struct ttm_bo_device {
 	struct list_head device_list;
 	struct ttm_bo_global *glob;
 	struct ttm_bo_driver *driver;
-	rwlock_t vm_lock;
 	struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES];
 	spinlock_t fence_lock;
+
 	/*
-	 * Protected by the vm lock.
+	 * Protected by internal locks.
 	 */
-	struct rb_root addr_space_rb;
-	struct drm_mm addr_space_mm;
+	struct drm_vma_offset_manager vma_manager;
 
 	/*
 	 * Protected by the global:lru lock.
-- 
1.8.3.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v4 4/4] drm/vma: provide drm_vma_node_unmap() helper
  2013-07-23 12:47 ` [PATCH v4 0/4] Unified VMA Offset Manager David Herrmann
                     ` (2 preceding siblings ...)
  2013-07-23 12:47   ` [PATCH v4 3/4] drm/ttm: convert to unified vma offset manager David Herrmann
@ 2013-07-23 12:47   ` David Herrmann
  2013-07-24 15:58     ` Daniel Vetter
  2013-07-24 19:10     ` [PATCH v5 " David Herrmann
  3 siblings, 2 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-23 12:47 UTC (permalink / raw)
  To: dri-devel; +Cc: Dave Airlie, Thomas Hellstrom, Daniel Vetter

Instead of unmapping the nodes in TTM and GEM users manually, we provide
a generic wrapper which does the correct thing for all vma-nodes.

v2: remove bdev->dev_mapping test in ttm_bo_unmap_virtual_unlocked() as
ttm_mem_io_free_vm() does nothing in that case (io_reserved_vm is 0).
v4: Fix docbook comments

Cc: Dave Airlie <airlied@redhat.com>
Cc: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Cc: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---
 drivers/gpu/drm/i915/i915_gem.c |  6 +-----
 drivers/gpu/drm/ttm/ttm_bo.c    | 11 +----------
 include/drm/drm_vma_manager.h   | 22 ++++++++++++++++++++++
 3 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 53f81b3..8673a00 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1427,11 +1427,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
 	if (!obj->fault_mappable)
 		return;
 
-	if (obj->base.dev->dev_mapping)
-		unmap_mapping_range(obj->base.dev->dev_mapping,
-				    (loff_t)drm_vma_node_offset_addr(&obj->base.vma_node),
-				    obj->base.size, 1);
-
+	drm_vma_node_unmap(&obj->base.vma_node, obj->base.dev->dev_mapping);
 	obj->fault_mappable = false;
 }
 
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 3dc08b6..050edfa 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1488,17 +1488,8 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
 void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo)
 {
 	struct ttm_bo_device *bdev = bo->bdev;
-	loff_t offset, holelen;
 
-	if (!bdev->dev_mapping)
-		return;
-
-	if (drm_vma_node_has_offset(&bo->vma_node)) {
-		offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node);
-		holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
-
-		unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
-	}
+	drm_vma_node_unmap(&bo->vma_node, bdev->dev_mapping);
 	ttm_mem_io_free_vm(bo);
 }
 
diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h
index ace2925..2772192 100644
--- a/include/drm/drm_vma_manager.h
+++ b/include/drm/drm_vma_manager.h
@@ -24,6 +24,7 @@
  */
 
 #include <drm/drm_mm.h>
+#include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/rbtree.h>
 #include <linux/spinlock.h>
@@ -177,4 +178,25 @@ static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
 	return ((__u64)node->vm_node.start) << PAGE_SHIFT;
 }
 
+/**
+ * drm_vma_node_unmap() - Unmap offset node
+ * @node: Offset node
+ * @file_mapping: Address space to unmap @node from
+ *
+ * Unmap all userspace mappings for a given offset node. The mappings must be
+ * associated with the @file_mapping address-space. If no offset exists or
+ * the address-space is invalid, nothing is done.
+ *
+ * This call is unlocked. The caller must guarantee that drm_vma_offset_remove()
+ * is not called on this node concurrently.
+ */
+static inline void drm_vma_node_unmap(struct drm_vma_offset_node *node,
+				      struct address_space *file_mapping)
+{
+	if (file_mapping && drm_vma_node_has_offset(node))
+		unmap_mapping_range(file_mapping,
+				    drm_vma_node_offset_addr(node),
+				    node->vm_pages << PAGE_SHIFT, 1);
+}
+
 #endif /* __DRM_VMA_MANAGER_H__ */
-- 
1.8.3.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH v4 1/4] drm: add unified vma offset manager
  2013-07-23 12:47   ` [PATCH v4 1/4] drm: add unified vma offset manager David Herrmann
@ 2013-07-24 15:35     ` Daniel Vetter
  2013-07-24 16:10       ` David Herrmann
  2013-07-24 19:06     ` [PATCH v5 " David Herrmann
  1 sibling, 1 reply; 31+ messages in thread
From: Daniel Vetter @ 2013-07-24 15:35 UTC (permalink / raw)
  To: David Herrmann; +Cc: Dave Airlie, dri-devel, Daniel Vetter

On Tue, Jul 23, 2013 at 02:47:13PM +0200, David Herrmann wrote:
> If we want to map GPU memory into user-space, we need to linearize the
> addresses to not confuse mm-core. Currently, GEM and TTM both implement
> their own offset-managers to assign a pgoff to each object for user-space
> CPU access. GEM uses a hash-table, TTM uses an rbtree.
> 
> This patch provides a unified implementation that can be used to replace
> both. TTM allows partial mmaps with a given offset, so we cannot use
> hashtables as the start address may not be known at mmap time. Hence, we
> use the rbtree-implementation of TTM.
> 
> We could easily update drm_mm to use an rbtree instead of a linked list
> for it's object list and thus drop the rbtree from the vma-manager.
> However, this would slow down drm_mm object allocation for all other
> use-cases (rbtree insertion) and add another 4-8 bytes to each mm node.
> Hence, use the separate tree but allow for later migration.
> 
> This is a rewrite of the 2012-proposal by David Airlie <airlied@linux.ie>
> 
> v2:
>  - fix Docbook integration
>  - drop drm_mm_node_linked() and use drm_mm_node_allocated()
>  - remove unjustified likely/unlikely usage (but keep for rbtree paths)
>  - remove BUG_ON() as drm_mm already does that
>  - clarify page-based vs. byte-based addresses
>  - use drm_vma_node_reset() for initialization, too
> v4:
>  - allow external locking via drm_vma_offset_un/lock_lookup()
>  - add locked lookup helper drm_vma_offset_lookup_locked()
> 
> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
> ---
>  Documentation/DocBook/drm.tmpl    |   6 +
>  drivers/gpu/drm/Makefile          |   2 +-
>  drivers/gpu/drm/drm_vma_manager.c | 283 ++++++++++++++++++++++++++++++++++++++
>  include/drm/drm_vma_manager.h     | 180 ++++++++++++++++++++++++
>  4 files changed, 470 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/drm_vma_manager.c
>  create mode 100644 include/drm/drm_vma_manager.h
> 
> diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl
> index 7d1278e..87e22ec 100644
> --- a/Documentation/DocBook/drm.tmpl
> +++ b/Documentation/DocBook/drm.tmpl
> @@ -2212,6 +2212,12 @@ void intel_crt_init(struct drm_device *dev)
>  !Iinclude/drm/drm_rect.h
>  !Edrivers/gpu/drm/drm_rect.c
>      </sect2>
> +    <sect2>
> +      <title>VMA Offset Manager</title>
> +!Pdrivers/gpu/drm/drm_vma_manager.c vma offset manager
> +!Edrivers/gpu/drm/drm_vma_manager.c
> +!Iinclude/drm/drm_vma_manager.h
> +    </sect2>
>    </sect1>
>  
>    <!-- Internals: kms properties -->
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 801bcaf..d943b94 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -13,7 +13,7 @@ drm-y       :=	drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
>  		drm_crtc.o drm_modes.o drm_edid.o \
>  		drm_info.o drm_debugfs.o drm_encoder_slave.o \
>  		drm_trace_points.o drm_global.o drm_prime.o \
> -		drm_rect.o
> +		drm_rect.o drm_vma_manager.o
>  
>  drm-$(CONFIG_COMPAT) += drm_ioc32.o
>  drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
> diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c
> new file mode 100644
> index 0000000..27760bd
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_vma_manager.c
> @@ -0,0 +1,283 @@
> +/*
> + * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
> + * Copyright (c) 2012 David Airlie <airlied@linux.ie>
> + * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_mm.h>
> +#include <drm/drm_vma_manager.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/rbtree.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +
> +/**
> + * DOC: vma offset manager
> + *
> + * The vma-manager is responsible to map arbitrary driver-dependent memory
> + * regions into the linear user address-space. It provides offsets to the
> + * caller which can then be used on the address_space of the drm-device. It
> + * takes care to not overlap regions, size them appropriately and to not
> + * confuse mm-core by inconsistent fake vm_pgoff fields.
> + * Drivers shouldn't use this for object placement in VMEM. This manager should
> + * only be used to manage mappings into linear user-space VMs.
> + *
> + * We use drm_mm as backend to manage object allocations. But it is highly
> + * optimized for alloc/free calls, not lookups. Hence, we use an rb-tree to
> + * speed up offset lookups.
> + *
> + * You must not use multiple offset managers on a single address_space.
> + * Otherwise, mm-core will be unable to tear down memory mappings as the VM will
> + * no longer be linear. Please use VM_NONLINEAR in that case and implement your
> + * own offset managers.
> + *
> + * This offset manager works on page-based addresses. That is, every argument
> + * and return code (with the exception of drm_vma_node_offset_addr()) is given
> + * in number of pages, not number of bytes. That means, object sizes and offsets
> + * must always be page-aligned (as usual).
> + * If you want to get a valid byte-based user-space address for a given offset,
> + * please see drm_vma_node_offset_addr().
> + */
> +
> +/**
> + * drm_vma_offset_manager_init - Initialize new offset-manager
> + * @mgr: Manager object
> + * @page_offset: Offset of available memory area (page-based)
> + * @size: Size of available memory area (page-based)

Bikeshed on naming: s/memory area/address space range/. Feel free to
ignore.

> + *
> + * Initialize a new offset-manager. The offset and area size available for the
> + * manager are given as @page_offset and @size. Both are interpreted as
> + * page-numbers, not bytes.
> + *
> + * Adding/removing nodes from the manager is locked internally and protected
> + * against concurrent access. However, node allocation and destruction is left
> + * for the caller. While calling into the vma-manager, a given node must
> + * always be guaranteed to be referenced.
> + */
> +void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
> +				 unsigned long page_offset, unsigned long size)
> +{
> +	rwlock_init(&mgr->vm_lock);
> +	mgr->vm_addr_space_rb = RB_ROOT;
> +	drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size);
> +}
> +EXPORT_SYMBOL(drm_vma_offset_manager_init);
> +
> +/**
> + * drm_vma_offset_manager_destroy() - Destroy offset manager
> + * @mgr: Manager object
> + *
> + * Destroy an object manager which was previously created via
> + * drm_vma_offset_manager_init(). The caller must remove all allocated nodes
> + * before destroying the manager. Otherwise, drm_mm will refuse to free the
> + * requested resources.
> + *
> + * The manager must not be accessed after this function is called.
> + */
> +void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr)
> +{
> +	/* take the lock to protect against buggy drivers */
> +	write_lock(&mgr->vm_lock);
> +	drm_mm_takedown(&mgr->vm_addr_space_mm);
> +	write_unlock(&mgr->vm_lock);
> +}
> +EXPORT_SYMBOL(drm_vma_offset_manager_destroy);
> +
> +/**
> + * drm_vma_offset_lookup() - Find node in offset space
> + * @mgr: Manager object
> + * @start: Start address for object (page-based)
> + * @pages: Size of object (page-based)
> + *
> + * Find a node given a start address and object size. This returns the _best_
> + * match for the given node. That is, @start may point somewhere into a valid
> + * region and the given node will be returned, as long as the node spans the
> + * whole requested area (given the size in number of pages as @pages).
> + *
> + * RETURNS:
> + * Returns NULL if no suitable node can be found. Otherwise, the best match
> + * is returned. It's the caller's responsibility to make sure the node doesn't
> + * get destroyed before the caller can access it.
> + */
> +struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr,
> +						  unsigned long start,
> +						  unsigned long pages)
> +{
> +	struct drm_vma_offset_node *node;
> +
> +	read_lock(&mgr->vm_lock);
> +	node = drm_vma_offset_lookup_locked(mgr, start, pages);
> +	read_unlock(&mgr->vm_lock);
> +
> +	return node;
> +}
> +EXPORT_SYMBOL(drm_vma_offset_lookup);
> +
> +/**
> + * drm_vma_offset_lookup_locked() - Find node in offset space
> + * @mgr: Manager object
> + * @start: Start address for object (page-based)
> + * @pages: Size of object (page-based)
> + *
> + * Same as drm_vma_offset_lookup() but requires the caller to lock offset lookup
> + * manually. See drm_vma_offset_lock_lookup() for an example.
> + *
> + * RETURNS:
> + * Returns NULL if no suitable node can be found. Otherwise, the best match
> + * is returned.
> + */
> +struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
> +							 unsigned long start,
> +							 unsigned long pages)
> +{
> +	struct drm_vma_offset_node *node, *best;
> +	struct rb_node *iter;
> +	unsigned long offset;
> +
> +	iter = mgr->vm_addr_space_rb.rb_node;
> +	best = NULL;
> +
> +	while (likely(iter)) {
> +		node = rb_entry(iter, struct drm_vma_offset_node, vm_rb);
> +		offset = node->vm_node.start;
> +		if (start >= offset) {
> +			iter = iter->rb_right;
> +			best = node;
> +			if (start == offset)
> +				break;
> +		} else {
> +			iter = iter->rb_left;
> +		}
> +	}
> +
> +	/* verify that the node spans the requested area */
> +	if (best) {
> +		offset = best->vm_node.start + best->vm_pages;
> +		if (offset > start + pages)
> +			best = NULL;
> +	}
> +
> +	return best;
> +}
> +EXPORT_SYMBOL(drm_vma_offset_lookup_locked);
> +
> +/* internal helper to link @node into the rb-tree */
> +static void _drm_vma_offset_add_rb(struct drm_vma_offset_manager *mgr,
> +				   struct drm_vma_offset_node *node)
> +{
> +	struct rb_node **iter = &mgr->vm_addr_space_rb.rb_node;
> +	struct rb_node *parent = NULL;
> +	struct drm_vma_offset_node *iter_node;
> +
> +	while (likely(*iter)) {
> +		parent = *iter;
> +		iter_node = rb_entry(*iter, struct drm_vma_offset_node, vm_rb);
> +
> +		if (node->vm_node.start < iter_node->vm_node.start)
> +			iter = &(*iter)->rb_left;
> +		else if (node->vm_node.start > iter_node->vm_node.start)
> +			iter = &(*iter)->rb_right;
> +		else
> +			BUG();
> +	}
> +
> +	rb_link_node(&node->vm_rb, parent, iter);
> +	rb_insert_color(&node->vm_rb, &mgr->vm_addr_space_rb);
> +}
> +
> +/**
> + * drm_vma_offset_add() - Add offset node to manager
> + * @mgr: Manager object
> + * @node: Node to be added
> + * @pages: Allocation size visible to user-space (in number of pages)
> + *
> + * Add a node to the offset-manager. If the node was already added, this does
> + * nothing and return 0. @pages is the size of the object given in number of
> + * pages.
> + * After this call succeeds, you can access the offset of the node until it
> + * is removed again.
> + *
> + * If this call fails, it is safe to retry the operation or call
> + * drm_vma_offset_remove(), anyway. However, no cleanup is required in that
> + * case.
> + *
> + * @pages is not required to be the same size as the underlying memory object
> + * that you want to map. It only limits the size that user-space can map into
> + * their address space.
> + *
> + * RETURNS:
> + * 0 on success, negative error code on failure.
> + */
> +int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
> +		       struct drm_vma_offset_node *node, unsigned long pages)
> +{
> +	int ret;
> +
> +	write_lock(&mgr->vm_lock);
> +
> +	if (drm_mm_node_allocated(&node->vm_node)) {
> +		ret = 0;
> +		goto out_unlock;
> +	}
> +
> +	ret = drm_mm_insert_node_generic(&mgr->vm_addr_space_mm,
> +					 &node->vm_node, pages, 0, 0);
> +	if (ret)
> +		goto out_unlock;
> +
> +	node->vm_pages = pages;

Why not just use node->vm_node.size and remove vm_pages?

> +	_drm_vma_offset_add_rb(mgr, node);
> +	goto out_unlock;

Unnecessary goto.

> +
> +out_unlock:
> +	write_unlock(&mgr->vm_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL(drm_vma_offset_add);
> +
> +/**
> + * drm_vma_offset_remove() - Remove offset node from manager
> + * @mgr: Manager object
> + * @node: Node to be removed
> + *
> + * Remove a node from the offset manager. If the node wasn't added before, this
> + * does nothing. After this call returns, the offset of the node must be not
> + * accessed, anymore.

I think we could clarify this a little "After this call returns the offset
node must not be used until a new offset is allocatd with
drm_vma_offset_add() again. Helper functions like
drm_vma_node_offset_addr() will return invalid data"

> + * It is safe to add the node via drm_vma_offset_add() again.
> + */
> +void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
> +			   struct drm_vma_offset_node *node)
> +{
> +	write_lock(&mgr->vm_lock);
> +
> +	if (drm_mm_node_allocated(&node->vm_node)) {
> +		rb_erase(&node->vm_rb, &mgr->vm_addr_space_rb);
> +		drm_mm_remove_node(&node->vm_node);
> +		memset(&node->vm_node, 0, sizeof(node->vm_node));
> +		node->vm_pages = 0;
> +	}
> +
> +	write_unlock(&mgr->vm_lock);
> +}
> +EXPORT_SYMBOL(drm_vma_offset_remove);
> diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h
> new file mode 100644
> index 0000000..ace2925
> --- /dev/null
> +++ b/include/drm/drm_vma_manager.h
> @@ -0,0 +1,180 @@
> +#ifndef __DRM_VMA_MANAGER_H__
> +#define __DRM_VMA_MANAGER_H__
> +
> +/*
> + * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#include <drm/drm_mm.h>
> +#include <linux/module.h>
> +#include <linux/rbtree.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +
> +struct drm_vma_offset_node {
> +	struct drm_mm_node vm_node;
> +	struct rb_node vm_rb;
> +	unsigned long vm_pages;
> +};
> +
> +struct drm_vma_offset_manager {
> +	rwlock_t vm_lock;
> +	struct rb_root vm_addr_space_rb;
> +	struct drm_mm vm_addr_space_mm;
> +};
> +
> +void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
> +				 unsigned long page_offset, unsigned long size);
> +void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr);
> +
> +struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr,
> +						  unsigned long start,
> +						  unsigned long pages);
> +struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
> +							   unsigned long start,
> +							   unsigned long pages);
> +int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
> +		       struct drm_vma_offset_node *node, unsigned long pages);
> +void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
> +			   struct drm_vma_offset_node *node);
> +
> +/**
> + * drm_vma_offset_exact_lookup() - Look up node by exact address
> + * @mgr: Manager object
> + * @start: Start address (page-based, not byte-based)
> + *
> + * Same as drm_vma_offset_lookup() but does not allow any offset into the node.
> + * It only returns the exact object with the given start address.
> + *
> + * RETURNS:
> + * Node at exact start address @start.
> + */
> +static inline struct drm_vma_offset_node *
> +drm_vma_offset_exact_lookup(struct drm_vma_offset_manager *mgr,
> +			    unsigned long start)
> +{
> +	return drm_vma_offset_lookup(mgr, start, 1);
> +}
> +
> +/**
> + * drm_vma_offset_lock_lookup() - Lock lookup for extended private use
> + * @mgr: Manager object
> + *
> + * Lock VMA manager for extended lookups. Only *_locked() VMA function calls
> + * are allowed while holding this lock. All other contexts are blocked from VMA
> + * until the lock is released via drm_vma_offset_unlock_lookup().
> + *
> + * Use this if you need to take a reference to the objects returned by
> + * drm_vma_offset_lookup_locked() before releasing this lock again.
> + *
> + * This lock must not be used for anything else than extended lookups. You must
> + * not call any other VMA helpers while holding this lock.
> + *
> + * Note: You're in atomic-context while holding this lock!
> + *
> + * Example:
> + *   drm_vma_offset_lock_lookup(mgr);
> + *   node = drm_vma_offset_lookup_locked(mgr);
> + *   if (node)
> + *       kref_get_unless_zero(container_of(node, sth, entr));
> + *   drm_vma_offset_unlock_lookup(mgr);
> + */
> +static inline void drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr)
> +{
> +	read_lock(&mgr->vm_lock);
> +}
> +
> +/**
> + * drm_vma_offset_unlock_lookup() - Unlock lookup for extended private use
> + * @mgr: Manager object
> + *
> + * Release lookup-lock. See drm_vma_offset_lock_lookup() for more information.
> + */
> +static inline void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr)
> +{
> +	read_unlock(&mgr->vm_lock);
> +}
> +
> +/**
> + * drm_vma_node_reset() - Initialize or reset node object
> + * @node: Node to initialize or reset
> + *
> + * Reset a node to its initial state. You must call this before using @node with
> + * any vma offset manager.

The must here feels a bit strong since this is just a memset and pretty
much everyone allocates with kzalloc. So calling this is redundant. Maybe

"This must be called if @node isn't already cleared (e.g. by allocating
with kzalloc) before it using it with any vma offset manager."

> + *
> + * This must not be called on an already allocated node, or you will leak
> + * memory.
> + */
> +static inline void drm_vma_node_reset(struct drm_vma_offset_node *node)
> +{
> +	memset(node, 0, sizeof(*node));
> +}
> +
> +/**
> + * drm_vma_node_start() - Return start address for page-based addressing
> + * @node: Node to inspect
> + *
> + * Return the start address of the given node. This can be used as offset into
> + * the linear VM space that is provided by the VMA offset manager. Note that
> + * this can only be used for page-based addressing. If you need a proper offset
> + * for user-space mappings, you must apply "<< PAGE_SHIFT" or use the
> + * drm_vma_node_offset_addr() helper instead.
> + *
> + * RETURNS:
> + * Start address of @node for page-based addressing. 0 if the node does not
> + * have an offset allocated.
> + */
> +static inline unsigned long drm_vma_node_start(struct drm_vma_offset_node *node)
> +{
> +	return node->vm_node.start;
> +}
> +
> +/**
> + * drm_vma_node_has_offset() - Check whether node is added to offset manager
> + * @node: Node to be checked
> + *
> + * RETURNS:
> + * true iff the node was previously allocated an offset and added to
> + * an vma offset manager.
> + */
> +static inline bool drm_vma_node_has_offset(struct drm_vma_offset_node *node)
> +{
> +	return drm_mm_node_allocated(&node->vm_node);
> +}
> +
> +/**
> + * drm_vma_node_offset_addr() - Return sanitized offset for user-space mmaps
> + * @node: Linked offset node
> + *
> + * Same as drm_vma_node_start() but returns the address as a valid offset that
> + * can be used for user-space mappings during mmap().
> + * This must not be called on unlinked nodes.
> + *
> + * RETURNS:
> + * Offset of @node for byte-based addressing. 0 if the node does not have an
> + * object allocated.
> + */
> +static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
> +{
> +	return ((__u64)node->vm_node.start) << PAGE_SHIFT;
> +}
> +
> +#endif /* __DRM_VMA_MANAGER_H__ */

With the above tiny bikesheds addressed this is:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v4 2/4] drm/gem: convert to new unified vma manager
  2013-07-23 12:47   ` [PATCH v4 2/4] drm/gem: convert to new unified vma manager David Herrmann
@ 2013-07-24 15:52     ` Daniel Vetter
  2013-07-24 16:27       ` David Herrmann
  2013-07-24 19:07     ` [PATCH v5 " David Herrmann
  1 sibling, 1 reply; 31+ messages in thread
From: Daniel Vetter @ 2013-07-24 15:52 UTC (permalink / raw)
  To: David Herrmann; +Cc: Daniel Vetter, dri-devel, Dave Airlie

On Tue, Jul 23, 2013 at 02:47:14PM +0200, David Herrmann wrote:
> Use the new vma manager instead of the old hashtable. Also convert all
> drivers to use the new convenience helpers. This drops all the
> (map_list.hash.key << PAGE_SHIFT) non-sense.
> 
> Locking and access-management is exactly the same as before with an
> additional lock inside of the vma-manager, which strictly wouldn't be
> needed for gem.
> 
> v2:
>  - rebase on drm-next
>  - init nodes via drm_vma_node_reset() in drm_gem.c
> v3:
>  - fix tegra
> v4:
>  - remove duplicate if (drm_vma_node_has_offset()) checks
>  - inline now trivial drm_vma_node_offset_addr() calls
> 
> Cc: Inki Dae <inki.dae@samsung.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Cc: Rob Clark <robdclark@gmail.com>
> Cc: Dave Airlie <airlied@redhat.com>
> Cc: Thierry Reding <thierry.reding@gmail.com>
> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
> Acked-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
> ---
>  drivers/gpu/drm/drm_gem.c                  | 89 +++++-------------------------
>  drivers/gpu/drm/drm_gem_cma_helper.c       | 16 ++----
>  drivers/gpu/drm/exynos/exynos_drm_gem.c    | 14 ++---
>  drivers/gpu/drm/gma500/gem.c               | 15 ++---
>  drivers/gpu/drm/i915/i915_gem.c            | 10 ++--
>  drivers/gpu/drm/omapdrm/omap_gem.c         | 28 +++++-----
>  drivers/gpu/drm/omapdrm/omap_gem_helpers.c | 49 +---------------
>  drivers/gpu/drm/udl/udl_gem.c              | 13 ++---
>  drivers/gpu/host1x/drm/gem.c               |  5 +-
>  include/drm/drmP.h                         |  7 +--
>  include/uapi/drm/drm.h                     |  2 +-
>  11 files changed, 62 insertions(+), 186 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
> index 1ad9e7e..8d3cdf3 100644
> --- a/drivers/gpu/drm/drm_gem.c
> +++ b/drivers/gpu/drm/drm_gem.c
> @@ -37,6 +37,7 @@
>  #include <linux/shmem_fs.h>
>  #include <linux/dma-buf.h>
>  #include <drm/drmP.h>
> +#include <drm/drm_vma_manager.h>
>  
>  /** @file drm_gem.c
>   *
> @@ -102,14 +103,9 @@ drm_gem_init(struct drm_device *dev)
>  	}
>  
>  	dev->mm_private = mm;
> -
> -	if (drm_ht_create(&mm->offset_hash, 12)) {
> -		kfree(mm);
> -		return -ENOMEM;
> -	}
> -
> -	drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START,
> -		    DRM_FILE_PAGE_OFFSET_SIZE);
> +	drm_vma_offset_manager_init(&mm->vma_manager,
> +				    DRM_FILE_PAGE_OFFSET_START,
> +				    DRM_FILE_PAGE_OFFSET_SIZE);
>  
>  	return 0;
>  }
> @@ -119,8 +115,7 @@ drm_gem_destroy(struct drm_device *dev)
>  {
>  	struct drm_gem_mm *mm = dev->mm_private;
>  
> -	drm_mm_takedown(&mm->offset_manager);
> -	drm_ht_remove(&mm->offset_hash);
> +	drm_vma_offset_manager_destroy(&mm->vma_manager);
>  	kfree(mm);
>  	dev->mm_private = NULL;
>  }
> @@ -160,6 +155,7 @@ void drm_gem_private_object_init(struct drm_device *dev,
>  
>  	kref_init(&obj->refcount);
>  	atomic_set(&obj->handle_count, 0);
> +	drm_vma_node_reset(&obj->vma_node);

We already presume (but it's not really documented) that a gem object is
cleared already (or allocated with kzalloc) so feels a bit like overhead.

>  	obj->size = size;
>  }
>  EXPORT_SYMBOL(drm_gem_private_object_init);
> @@ -302,12 +298,8 @@ drm_gem_free_mmap_offset(struct drm_gem_object *obj)
>  {
>  	struct drm_device *dev = obj->dev;
>  	struct drm_gem_mm *mm = dev->mm_private;
> -	struct drm_map_list *list = &obj->map_list;
>  
> -	drm_ht_remove_item(&mm->offset_hash, &list->hash);
> -	drm_mm_put_block(list->file_offset_node);
> -	kfree(list->map);
> -	list->map = NULL;
> +	drm_vma_offset_remove(&mm->vma_manager, &obj->vma_node);
>  }
>  EXPORT_SYMBOL(drm_gem_free_mmap_offset);
>  
> @@ -327,54 +319,9 @@ drm_gem_create_mmap_offset(struct drm_gem_object *obj)
>  {
>  	struct drm_device *dev = obj->dev;
>  	struct drm_gem_mm *mm = dev->mm_private;
> -	struct drm_map_list *list;
> -	struct drm_local_map *map;
> -	int ret;
> -
> -	/* Set the object up for mmap'ing */
> -	list = &obj->map_list;
> -	list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
> -	if (!list->map)
> -		return -ENOMEM;
> -
> -	map = list->map;
> -	map->type = _DRM_GEM;
> -	map->size = obj->size;
> -	map->handle = obj;
> -
> -	/* Get a DRM GEM mmap offset allocated... */
> -	list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
> -			obj->size / PAGE_SIZE, 0, false);
> -
> -	if (!list->file_offset_node) {
> -		DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
> -		ret = -ENOSPC;
> -		goto out_free_list;
> -	}
>  
> -	list->file_offset_node = drm_mm_get_block(list->file_offset_node,
> -			obj->size / PAGE_SIZE, 0);
> -	if (!list->file_offset_node) {
> -		ret = -ENOMEM;
> -		goto out_free_list;
> -	}
> -
> -	list->hash.key = list->file_offset_node->start;
> -	ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
> -	if (ret) {
> -		DRM_ERROR("failed to add to map hash\n");
> -		goto out_free_mm;
> -	}
> -
> -	return 0;
> -
> -out_free_mm:
> -	drm_mm_put_block(list->file_offset_node);
> -out_free_list:
> -	kfree(list->map);
> -	list->map = NULL;
> -
> -	return ret;
> +	return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node,
> +				  obj->size / PAGE_SIZE);
>  }
>  EXPORT_SYMBOL(drm_gem_create_mmap_offset);
>  
> @@ -703,8 +650,8 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
>  	struct drm_file *priv = filp->private_data;
>  	struct drm_device *dev = priv->minor->dev;
>  	struct drm_gem_mm *mm = dev->mm_private;
> -	struct drm_local_map *map = NULL;
> -	struct drm_hash_item *hash;
> +	struct drm_gem_object *obj;
> +	struct drm_vma_offset_node *node;
>  	int ret = 0;
>  
>  	if (drm_device_is_unplugged(dev))
> @@ -712,21 +659,15 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
>  
>  	mutex_lock(&dev->struct_mutex);
>  
> -	if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
> +	node = drm_vma_offset_exact_lookup(&mm->vma_manager, vma->vm_pgoff);
> +	if (!node) {
>  		mutex_unlock(&dev->struct_mutex);
>  		return drm_mmap(filp, vma);
>  	}
>  
> -	map = drm_hash_entry(hash, struct drm_map_list, hash)->map;
> -	if (!map ||
> -	    ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) {
> -		ret =  -EPERM;
> -		goto out_unlock;
> -	}
> -
> -	ret = drm_gem_mmap_obj(map->handle, map->size, vma);
> +	obj = container_of(node, struct drm_gem_object, vma_node);
> +	ret = drm_gem_mmap_obj(obj, node->vm_pages, vma);

Iirc the old code only resulted in a ht hit if the start of the mmap
regioni exactly matched the offset. The new code (I think at least, maybe
I've misread it a bit) will return an object even if the start isn't
aligned. drm_gem_mmap_obj checks already that the object is big enough but
we don't check the start any more. I think this should be readded.

It's a bit funny that gem wants an exact match but is ok with just mapping
the beginning of the object, but I guess that abi is now enshrined ;-)

>  
> -out_unlock:
>  	mutex_unlock(&dev->struct_mutex);
>  
>  	return ret;
> diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
> index ece72a8..bf09a6dc 100644
> --- a/drivers/gpu/drm/drm_gem_cma_helper.c
> +++ b/drivers/gpu/drm/drm_gem_cma_helper.c
> @@ -27,11 +27,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm.h>
>  #include <drm/drm_gem_cma_helper.h>
> -
> -static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
> -{
> -	return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
> -}
> +#include <drm/drm_vma_manager.h>
>  
>  /*
>   * __drm_gem_cma_create - Create a GEM CMA object without allocating memory
> @@ -172,8 +168,7 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
>  {
>  	struct drm_gem_cma_object *cma_obj;
>  
> -	if (gem_obj->map_list.map)
> -		drm_gem_free_mmap_offset(gem_obj);
> +	drm_gem_free_mmap_offset(gem_obj);
>  
>  	cma_obj = to_drm_gem_cma_obj(gem_obj);
>  
> @@ -237,7 +232,7 @@ int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
>  		return -EINVAL;
>  	}
>  
> -	*offset = get_gem_mmap_offset(gem_obj);
> +	*offset = (unsigned int)drm_vma_node_offset_addr(&gem_obj->vma_node);

*offset is an u64, same as drm_vma_node_offset_addr. Why do we need a cast
here?

>  
>  	drm_gem_object_unreference(gem_obj);
>  
> @@ -301,12 +296,11 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m
>  {
>  	struct drm_gem_object *obj = &cma_obj->base;
>  	struct drm_device *dev = obj->dev;
> -	uint64_t off = 0;
> +	uint64_t off;
>  
>  	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
>  
> -	if (obj->map_list.map)
> -		off = (uint64_t)obj->map_list.hash.key;
> +	off = drm_vma_node_start(&obj->vma_node);
>  
>  	seq_printf(m, "%2d (%2d) %08llx %08Zx %p %d",
>  			obj->name, obj->refcount.refcount.counter,
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
> index 24c22a8..be32db1 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
> @@ -10,6 +10,7 @@
>   */
>  
>  #include <drm/drmP.h>
> +#include <drm/drm_vma_manager.h>
>  
>  #include <linux/shmem_fs.h>
>  #include <drm/exynos_drm.h>
> @@ -152,8 +153,7 @@ out:
>  	exynos_drm_fini_buf(obj->dev, buf);
>  	exynos_gem_obj->buffer = NULL;
>  
> -	if (obj->map_list.map)
> -		drm_gem_free_mmap_offset(obj);
> +	drm_gem_free_mmap_offset(obj);
>  
>  	/* release file pointer to gem object. */
>  	drm_gem_object_release(obj);
> @@ -703,13 +703,11 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
>  		goto unlock;
>  	}
>  
> -	if (!obj->map_list.map) {
> -		ret = drm_gem_create_mmap_offset(obj);
> -		if (ret)
> -			goto out;
> -	}
> +	ret = drm_gem_create_mmap_offset(obj);
> +	if (ret)
> +		goto out;
>  
> -	*offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
> +	*offset = drm_vma_node_offset_addr(&obj->vma_node);
>  	DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
>  
>  out:
> diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c
> index fe1d332..2f77bea 100644
> --- a/drivers/gpu/drm/gma500/gem.c
> +++ b/drivers/gpu/drm/gma500/gem.c
> @@ -26,6 +26,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm.h>
>  #include <drm/gma_drm.h>
> +#include <drm/drm_vma_manager.h>
>  #include "psb_drv.h"
>  
>  int psb_gem_init_object(struct drm_gem_object *obj)
> @@ -38,8 +39,7 @@ void psb_gem_free_object(struct drm_gem_object *obj)
>  	struct gtt_range *gtt = container_of(obj, struct gtt_range, gem);
>  
>  	/* Remove the list map if one is present */
> -	if (obj->map_list.map)
> -		drm_gem_free_mmap_offset(obj);
> +	drm_gem_free_mmap_offset(obj);
>  	drm_gem_object_release(obj);
>  
>  	/* This must occur last as it frees up the memory of the GEM object */
> @@ -81,13 +81,10 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev,
>  	/* What validation is needed here ? */
>  
>  	/* Make it mmapable */
> -	if (!obj->map_list.map) {
> -		ret = drm_gem_create_mmap_offset(obj);
> -		if (ret)
> -			goto out;
> -	}
> -	/* GEM should really work out the hash offsets for us */
> -	*offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
> +	ret = drm_gem_create_mmap_offset(obj);
> +	if (ret)
> +		goto out;
> +	*offset = drm_vma_node_offset_addr(&obj->vma_node);
>  out:
>  	drm_gem_object_unreference(obj);
>  unlock:
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 46bf7e3..53f81b3 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -26,6 +26,7 @@
>   */
>  
>  #include <drm/drmP.h>
> +#include <drm/drm_vma_manager.h>
>  #include <drm/i915_drm.h>
>  #include "i915_drv.h"
>  #include "i915_trace.h"
> @@ -1428,7 +1429,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
>  
>  	if (obj->base.dev->dev_mapping)
>  		unmap_mapping_range(obj->base.dev->dev_mapping,
> -				    (loff_t)obj->base.map_list.hash.key<<PAGE_SHIFT,
> +				    (loff_t)drm_vma_node_offset_addr(&obj->base.vma_node),
>  				    obj->base.size, 1);
>  
>  	obj->fault_mappable = false;
> @@ -1486,7 +1487,7 @@ static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj)
>  	struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
>  	int ret;
>  
> -	if (obj->base.map_list.map)
> +	if (drm_vma_node_has_offset(&obj->base.vma_node))
>  		return 0;
>  
>  	dev_priv->mm.shrinker_no_lock_stealing = true;
> @@ -1517,9 +1518,6 @@ out:
>  
>  static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj)
>  {
> -	if (!obj->base.map_list.map)
> -		return;
> -
>  	drm_gem_free_mmap_offset(&obj->base);
>  }
>  
> @@ -1558,7 +1556,7 @@ i915_gem_mmap_gtt(struct drm_file *file,
>  	if (ret)
>  		goto out;
>  
> -	*offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT;
> +	*offset = drm_vma_node_offset_addr(&obj->base.vma_node);
>  
>  out:
>  	drm_gem_object_unreference(&obj->base);
> diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
> index cbcd71e..f90531fc00 100644
> --- a/drivers/gpu/drm/omapdrm/omap_gem.c
> +++ b/drivers/gpu/drm/omapdrm/omap_gem.c
> @@ -20,6 +20,7 @@
>  
>  #include <linux/spinlock.h>
>  #include <linux/shmem_fs.h>
> +#include <drm/drm_vma_manager.h>
>  
>  #include "omap_drv.h"
>  #include "omap_dmm_tiler.h"
> @@ -308,21 +309,20 @@ uint32_t omap_gem_flags(struct drm_gem_object *obj)
>  static uint64_t mmap_offset(struct drm_gem_object *obj)
>  {
>  	struct drm_device *dev = obj->dev;
> +	int ret;
> +	size_t size;
>  
>  	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
>  
> -	if (!obj->map_list.map) {
> -		/* Make it mmapable */
> -		size_t size = omap_gem_mmap_size(obj);
> -		int ret = _drm_gem_create_mmap_offset_size(obj, size);
> -
> -		if (ret) {
> -			dev_err(dev->dev, "could not allocate mmap offset\n");
> -			return 0;
> -		}
> +	/* Make it mmapable */
> +	size = omap_gem_mmap_size(obj);
> +	ret = _drm_gem_create_mmap_offset_size(obj, size);
> +	if (ret) {
> +		dev_err(dev->dev, "could not allocate mmap offset\n");
> +		return 0;
>  	}
>  
> -	return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT;
> +	return drm_vma_node_offset_addr(&obj->vma_node);
>  }
>  
>  uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
> @@ -997,12 +997,11 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
>  {
>  	struct drm_device *dev = obj->dev;
>  	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> -	uint64_t off = 0;
> +	uint64_t off;
>  
>  	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
>  
> -	if (obj->map_list.map)
> -		off = (uint64_t)obj->map_list.hash.key;
> +	off = drm_vma_node_start(&obj->vma_node);
>  
>  	seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d",
>  			omap_obj->flags, obj->name, obj->refcount.refcount.counter,
> @@ -1309,8 +1308,7 @@ void omap_gem_free_object(struct drm_gem_object *obj)
>  
>  	list_del(&omap_obj->mm_list);
>  
> -	if (obj->map_list.map)
> -		drm_gem_free_mmap_offset(obj);
> +	drm_gem_free_mmap_offset(obj);
>  
>  	/* this means the object is still pinned.. which really should
>  	 * not happen.  I think..
> diff --git a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
> index f9eb679..dbb1575 100644
> --- a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
> +++ b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
> @@ -118,52 +118,7 @@ _drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size)
>  {
>  	struct drm_device *dev = obj->dev;
>  	struct drm_gem_mm *mm = dev->mm_private;
> -	struct drm_map_list *list;
> -	struct drm_local_map *map;
> -	int ret = 0;
> -
> -	/* Set the object up for mmap'ing */
> -	list = &obj->map_list;
> -	list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
> -	if (!list->map)
> -		return -ENOMEM;
> -
> -	map = list->map;
> -	map->type = _DRM_GEM;
> -	map->size = size;
> -	map->handle = obj;
> -
> -	/* Get a DRM GEM mmap offset allocated... */
> -	list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
> -			size / PAGE_SIZE, 0, 0);
> -
> -	if (!list->file_offset_node) {
> -		DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
> -		ret = -ENOSPC;
> -		goto out_free_list;
> -	}
> -
> -	list->file_offset_node = drm_mm_get_block(list->file_offset_node,
> -			size / PAGE_SIZE, 0);
> -	if (!list->file_offset_node) {
> -		ret = -ENOMEM;
> -		goto out_free_list;
> -	}
> -
> -	list->hash.key = list->file_offset_node->start;
> -	ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
> -	if (ret) {
> -		DRM_ERROR("failed to add to map hash\n");
> -		goto out_free_mm;
> -	}
> -
> -	return 0;
> -
> -out_free_mm:
> -	drm_mm_put_block(list->file_offset_node);
> -out_free_list:
> -	kfree(list->map);
> -	list->map = NULL;
>  
> -	return ret;
> +	return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node,
> +				  size / PAGE_SIZE);
>  }
> diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
> index ef034fa..2a4cb2f 100644
> --- a/drivers/gpu/drm/udl/udl_gem.c
> +++ b/drivers/gpu/drm/udl/udl_gem.c
> @@ -223,8 +223,7 @@ void udl_gem_free_object(struct drm_gem_object *gem_obj)
>  	if (obj->pages)
>  		udl_gem_put_pages(obj);
>  
> -	if (gem_obj->map_list.map)
> -		drm_gem_free_mmap_offset(gem_obj);
> +	drm_gem_free_mmap_offset(gem_obj);
>  }
>  
>  /* the dumb interface doesn't work with the GEM straight MMAP
> @@ -247,13 +246,11 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev,
>  	ret = udl_gem_get_pages(gobj, GFP_KERNEL);
>  	if (ret)
>  		goto out;
> -	if (!gobj->base.map_list.map) {
> -		ret = drm_gem_create_mmap_offset(obj);
> -		if (ret)
> -			goto out;
> -	}
> +	ret = drm_gem_create_mmap_offset(obj);
> +	if (ret)
> +		goto out;
>  
> -	*offset = (u64)gobj->base.map_list.hash.key << PAGE_SHIFT;
> +	*offset = drm_vma_node_offset_addr(&gobj->base.vma_node);
>  
>  out:
>  	drm_gem_object_unreference(&gobj->base);
> diff --git a/drivers/gpu/host1x/drm/gem.c b/drivers/gpu/host1x/drm/gem.c
> index c5e9a9b..bc323b3 100644
> --- a/drivers/gpu/host1x/drm/gem.c
> +++ b/drivers/gpu/host1x/drm/gem.c
> @@ -108,7 +108,7 @@ static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo)
>  
>  unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo)
>  {
> -	return (unsigned int)bo->gem.map_list.hash.key << PAGE_SHIFT;
> +	return (unsigned int)drm_vma_node_offset_addr(&bo->gem.vma_node);

Ok, tegra is pretty funny here about mmap offsets, using an int.
Whatever, pre-existing fanciness so not our problem.

>  }
>  
>  struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
> @@ -182,8 +182,7 @@ void tegra_bo_free_object(struct drm_gem_object *gem)
>  {
>  	struct tegra_bo *bo = to_tegra_bo(gem);
>  
> -	if (gem->map_list.map)
> -		drm_gem_free_mmap_offset(gem);
> +	drm_gem_free_mmap_offset(gem);
>  
>  	drm_gem_object_release(gem);
>  	tegra_bo_destroy(gem->dev, bo);
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index 0ab6a09..4b518e0 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -71,6 +71,7 @@
>  #include <asm/pgalloc.h>
>  #include <drm/drm.h>
>  #include <drm/drm_sarea.h>
> +#include <drm/drm_vma_manager.h>
>  
>  #include <linux/idr.h>
>  
> @@ -587,7 +588,6 @@ struct drm_map_list {
>  	struct drm_local_map *map;	/**< mapping */
>  	uint64_t user_token;
>  	struct drm_master *master;
> -	struct drm_mm_node *file_offset_node;	/**< fake offset */
>  };
>  
>  /**
> @@ -622,8 +622,7 @@ struct drm_ati_pcigart_info {
>   * GEM specific mm private for tracking GEM objects
>   */
>  struct drm_gem_mm {
> -	struct drm_mm offset_manager;	/**< Offset mgmt for buffer objects */
> -	struct drm_open_hash offset_hash; /**< User token hash table for maps */
> +	struct drm_vma_offset_manager vma_manager;
>  };
>  
>  /**
> @@ -644,7 +643,7 @@ struct drm_gem_object {
>  	struct file *filp;
>  
>  	/* Mapping info for this object */
> -	struct drm_map_list map_list;
> +	struct drm_vma_offset_node vma_node;
>  
>  	/**
>  	 * Size of the object, in bytes.  Immutable over the object's
> diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
> index 238a166..272580c 100644
> --- a/include/uapi/drm/drm.h
> +++ b/include/uapi/drm/drm.h
> @@ -181,7 +181,7 @@ enum drm_map_type {
>  	_DRM_AGP = 3,		  /**< AGP/GART */
>  	_DRM_SCATTER_GATHER = 4,  /**< Scatter/gather memory for PCI DMA */
>  	_DRM_CONSISTENT = 5,	  /**< Consistent memory for PCI DMA */
> -	_DRM_GEM = 6,		  /**< GEM object */
> +	_DRM_GEM = 6,		  /**< GEM object (obsolete) */
>  };
>  
>  /**
> -- 
> 1.8.3.3

A few small things, with those addressed this is:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

I guess with the common infrastructure we'll have a bit of room for
follow-up cleanups, but that can be done on especially rainy autumn days
;-)

Cheers, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v4 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-23 12:47   ` [PATCH v4 3/4] drm/ttm: convert to unified vma offset manager David Herrmann
@ 2013-07-24 15:57     ` Daniel Vetter
  2013-07-24 19:08     ` [PATCH v5 " David Herrmann
  1 sibling, 0 replies; 31+ messages in thread
From: Daniel Vetter @ 2013-07-24 15:57 UTC (permalink / raw)
  To: David Herrmann
  Cc: Thomas Hellstrom, Martin Peres, Daniel Vetter, dri-devel,
	Alex Deucher, Ben Skeggs, Dave Airlie

On Tue, Jul 23, 2013 at 02:47:15PM +0200, David Herrmann wrote:
> Use the new vma-manager infrastructure. This doesn't change any
> implementation details as the vma-offset-manager is nearly copied 1-to-1
> from TTM.
> 
> The vm_lock is moved into the offset manager so we can drop it from TTM.
> During lookup, we use the vma locking helpers to take a reference to the
> found object.
> In all other scenarios, locking stays the same as before. We always
> guarantee that drm_vma_offset_remove() is called only during destruction.
> Hence, helpers like drm_vma_node_offset_addr() are always safe as long as
> the node has a valid offset.
> 
> This also drops the addr_space_offset member as it is a copy of vm_start
> in vma_node objects. Use the accessor functions instead.
> 
> v4:
>  - remove vm_lock
>  - use drm_vma_offset_lock_lookup() to protect lookup (instead of vm_lock)
> 
> Cc: Dave Airlie <airlied@redhat.com>
> Cc: Ben Skeggs <bskeggs@redhat.com>
> Cc: Maarten Lankhorst <maarten.lankhorst@canonical.com>
> Cc: Martin Peres <martin.peres@labri.fr>
> Cc: Alex Deucher <alexander.deucher@amd.com>
> Cc: Thomas Hellstrom <thellstrom@vmware.com>
> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
> ---
>  drivers/gpu/drm/ast/ast_main.c            |  2 +-
>  drivers/gpu/drm/cirrus/cirrus_main.c      |  2 +-
>  drivers/gpu/drm/mgag200/mgag200_main.c    |  2 +-
>  drivers/gpu/drm/nouveau/nouveau_display.c |  2 +-
>  drivers/gpu/drm/nouveau/nouveau_gem.c     |  2 +-
>  drivers/gpu/drm/qxl/qxl_object.h          |  2 +-
>  drivers/gpu/drm/qxl/qxl_release.c         |  2 +-
>  drivers/gpu/drm/radeon/radeon_object.h    |  5 +-
>  drivers/gpu/drm/ttm/ttm_bo.c              | 89 ++++++-------------------------
>  drivers/gpu/drm/ttm/ttm_bo_util.c         |  3 +-
>  drivers/gpu/drm/ttm/ttm_bo_vm.c           | 81 +++++++++++-----------------
>  drivers/gpu/drm/vmwgfx/vmwgfx_resource.c  |  4 +-
>  include/drm/ttm/ttm_bo_api.h              | 15 ++----
>  include/drm/ttm/ttm_bo_driver.h           | 10 ++--
>  14 files changed, 66 insertions(+), 155 deletions(-)
> 
> diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
> index f60fd7b..c195dc2 100644
> --- a/drivers/gpu/drm/ast/ast_main.c
> +++ b/drivers/gpu/drm/ast/ast_main.c
> @@ -487,7 +487,7 @@ void ast_gem_free_object(struct drm_gem_object *obj)
>  
>  static inline u64 ast_bo_mmap_offset(struct ast_bo *bo)
>  {
> -	return bo->bo.addr_space_offset;
> +	return drm_vma_node_offset_addr(&bo->bo.vma_node);
>  }
>  int
>  ast_dumb_mmap_offset(struct drm_file *file,
> diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c
> index 35cbae8..3a7a0ef 100644
> --- a/drivers/gpu/drm/cirrus/cirrus_main.c
> +++ b/drivers/gpu/drm/cirrus/cirrus_main.c
> @@ -294,7 +294,7 @@ void cirrus_gem_free_object(struct drm_gem_object *obj)
>  
>  static inline u64 cirrus_bo_mmap_offset(struct cirrus_bo *bo)
>  {
> -	return bo->bo.addr_space_offset;
> +	return drm_vma_node_offset_addr(&bo->bo.vma_node);
>  }
>  
>  int
> diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c
> index 9fa5685..1a75ea3 100644
> --- a/drivers/gpu/drm/mgag200/mgag200_main.c
> +++ b/drivers/gpu/drm/mgag200/mgag200_main.c
> @@ -349,7 +349,7 @@ void mgag200_gem_free_object(struct drm_gem_object *obj)
>  
>  static inline u64 mgag200_bo_mmap_offset(struct mgag200_bo *bo)
>  {
> -	return bo->bo.addr_space_offset;
> +	return drm_vma_node_offset_addr(&bo->bo.vma_node);
>  }
>  
>  int
> diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
> index 708b2d1..7a8caa1 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_display.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_display.c
> @@ -697,7 +697,7 @@ nouveau_display_dumb_map_offset(struct drm_file *file_priv,
>  	gem = drm_gem_object_lookup(dev, file_priv, handle);
>  	if (gem) {
>  		struct nouveau_bo *bo = gem->driver_private;
> -		*poffset = bo->bo.addr_space_offset;
> +		*poffset = drm_vma_node_offset_addr(&bo->bo.vma_node);
>  		drm_gem_object_unreference_unlocked(gem);
>  		return 0;
>  	}
> diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
> index e72d09c..86597eb 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_gem.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
> @@ -226,7 +226,7 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem,
>  	}
>  
>  	rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
> -	rep->map_handle = nvbo->bo.addr_space_offset;
> +	rep->map_handle = drm_vma_node_offset_addr(&nvbo->bo.vma_node);
>  	rep->tile_mode = nvbo->tile_mode;
>  	rep->tile_flags = nvbo->tile_flags;
>  	return 0;
> diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h
> index ee7ad79..af10165 100644
> --- a/drivers/gpu/drm/qxl/qxl_object.h
> +++ b/drivers/gpu/drm/qxl/qxl_object.h
> @@ -59,7 +59,7 @@ static inline unsigned long qxl_bo_size(struct qxl_bo *bo)
>  
>  static inline u64 qxl_bo_mmap_offset(struct qxl_bo *bo)
>  {
> -	return bo->tbo.addr_space_offset;
> +	return drm_vma_node_offset_addr(&bo->tbo.vma_node);
>  }
>  
>  static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type,
> diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c
> index b443d67..1a648e1 100644
> --- a/drivers/gpu/drm/qxl/qxl_release.c
> +++ b/drivers/gpu/drm/qxl/qxl_release.c
> @@ -87,7 +87,7 @@ qxl_release_free(struct qxl_device *qdev,
>  
>  	for (i = 0 ; i < release->bo_count; ++i) {
>  		QXL_INFO(qdev, "release %llx\n",
> -			release->bos[i]->tbo.addr_space_offset
> +			drm_vma_node_offset_addr(&release->bos[i]->tbo.vma_node)
>  						- DRM_FILE_OFFSET);
>  		qxl_fence_remove_release(&release->bos[i]->fence, release->id);
>  		qxl_bo_unref(&release->bos[i]);
> diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
> index 49c82c4..209b111 100644
> --- a/drivers/gpu/drm/radeon/radeon_object.h
> +++ b/drivers/gpu/drm/radeon/radeon_object.h
> @@ -113,13 +113,10 @@ static inline unsigned radeon_bo_gpu_page_alignment(struct radeon_bo *bo)
>   * @bo:	radeon object for which we query the offset
>   *
>   * Returns mmap offset of the object.
> - *
> - * Note: addr_space_offset is constant after ttm bo init thus isn't protected
> - * by any lock.
>   */
>  static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo)
>  {
> -	return bo->tbo.addr_space_offset;
> +	return drm_vma_node_offset_addr(&bo->tbo.vma_node);
>  }
>  
>  extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
> index cb9dd67..3dc08b6 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> @@ -615,13 +615,7 @@ static void ttm_bo_release(struct kref *kref)
>  	struct ttm_bo_device *bdev = bo->bdev;
>  	struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type];
>  
> -	write_lock(&bdev->vm_lock);
> -	if (likely(bo->vm_node != NULL)) {
> -		rb_erase(&bo->vm_rb, &bdev->addr_space_rb);
> -		drm_mm_put_block(bo->vm_node);
> -		bo->vm_node = NULL;
> -	}
> -	write_unlock(&bdev->vm_lock);
> +	drm_vma_offset_remove(&bdev->vma_manager, &bo->vma_node);
>  	ttm_mem_io_lock(man, false);
>  	ttm_mem_io_free_vm(bo);
>  	ttm_mem_io_unlock(man);
> @@ -1129,6 +1123,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
>  	bo->resv = &bo->ttm_resv;
>  	reservation_object_init(bo->resv);
>  	atomic_inc(&bo->glob->bo_count);
> +	drm_vma_node_reset(&bo->vma_node);
>  
>  	ret = ttm_bo_check_placement(bo, placement);
>  
> @@ -1424,10 +1419,7 @@ int ttm_bo_device_release(struct ttm_bo_device *bdev)
>  		TTM_DEBUG("Swap list was clean\n");
>  	spin_unlock(&glob->lru_lock);
>  
> -	BUG_ON(!drm_mm_clean(&bdev->addr_space_mm));
> -	write_lock(&bdev->vm_lock);
> -	drm_mm_takedown(&bdev->addr_space_mm);
> -	write_unlock(&bdev->vm_lock);
> +	drm_vma_offset_manager_destroy(&bdev->vma_manager);
>  
>  	return ret;
>  }
> @@ -1441,7 +1433,6 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
>  {
>  	int ret = -EINVAL;
>  
> -	rwlock_init(&bdev->vm_lock);
>  	bdev->driver = driver;
>  
>  	memset(bdev->man, 0, sizeof(bdev->man));
> @@ -1454,9 +1445,8 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
>  	if (unlikely(ret != 0))
>  		goto out_no_sys;
>  
> -	bdev->addr_space_rb = RB_ROOT;
> -	drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000);
> -
> +	drm_vma_offset_manager_init(&bdev->vma_manager, file_page_offset,
> +				    0x10000000);
>  	INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue);
>  	INIT_LIST_HEAD(&bdev->ddestroy);
>  	bdev->dev_mapping = NULL;
> @@ -1498,12 +1488,17 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
>  void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo)
>  {
>  	struct ttm_bo_device *bdev = bo->bdev;
> -	loff_t offset = (loff_t) bo->addr_space_offset;
> -	loff_t holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
> +	loff_t offset, holelen;
>  
>  	if (!bdev->dev_mapping)
>  		return;
> -	unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
> +
> +	if (drm_vma_node_has_offset(&bo->vma_node)) {
> +		offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node);
> +		holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
> +
> +		unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
> +	}
>  	ttm_mem_io_free_vm(bo);
>  }
>  
> @@ -1520,31 +1515,6 @@ void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo)
>  
>  EXPORT_SYMBOL(ttm_bo_unmap_virtual);
>  
> -static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo)
> -{
> -	struct ttm_bo_device *bdev = bo->bdev;
> -	struct rb_node **cur = &bdev->addr_space_rb.rb_node;
> -	struct rb_node *parent = NULL;
> -	struct ttm_buffer_object *cur_bo;
> -	unsigned long offset = bo->vm_node->start;
> -	unsigned long cur_offset;
> -
> -	while (*cur) {
> -		parent = *cur;
> -		cur_bo = rb_entry(parent, struct ttm_buffer_object, vm_rb);
> -		cur_offset = cur_bo->vm_node->start;
> -		if (offset < cur_offset)
> -			cur = &parent->rb_left;
> -		else if (offset > cur_offset)
> -			cur = &parent->rb_right;
> -		else
> -			BUG();
> -	}
> -
> -	rb_link_node(&bo->vm_rb, parent, cur);
> -	rb_insert_color(&bo->vm_rb, &bdev->addr_space_rb);
> -}
> -
>  /**
>   * ttm_bo_setup_vm:
>   *
> @@ -1559,38 +1529,9 @@ static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo)
>  static int ttm_bo_setup_vm(struct ttm_buffer_object *bo)
>  {
>  	struct ttm_bo_device *bdev = bo->bdev;
> -	int ret;
> -
> -retry_pre_get:
> -	ret = drm_mm_pre_get(&bdev->addr_space_mm);
> -	if (unlikely(ret != 0))
> -		return ret;
> -
> -	write_lock(&bdev->vm_lock);
> -	bo->vm_node = drm_mm_search_free(&bdev->addr_space_mm,
> -					 bo->mem.num_pages, 0, 0);
> -
> -	if (unlikely(bo->vm_node == NULL)) {
> -		ret = -ENOMEM;
> -		goto out_unlock;
> -	}
>  
> -	bo->vm_node = drm_mm_get_block_atomic(bo->vm_node,
> -					      bo->mem.num_pages, 0);
> -
> -	if (unlikely(bo->vm_node == NULL)) {
> -		write_unlock(&bdev->vm_lock);
> -		goto retry_pre_get;
> -	}
> -
> -	ttm_bo_vm_insert_rb(bo);
> -	write_unlock(&bdev->vm_lock);
> -	bo->addr_space_offset = ((uint64_t) bo->vm_node->start) << PAGE_SHIFT;
> -
> -	return 0;
> -out_unlock:
> -	write_unlock(&bdev->vm_lock);
> -	return ret;
> +	return drm_vma_offset_add(&bdev->vma_manager, &bo->vma_node,
> +				  bo->mem.num_pages);
>  }
>  
>  int ttm_bo_wait(struct ttm_buffer_object *bo,
> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
> index 319cf41..7cc904d 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> @@ -30,6 +30,7 @@
>  
>  #include <drm/ttm/ttm_bo_driver.h>
>  #include <drm/ttm/ttm_placement.h>
> +#include <drm/drm_vma_manager.h>
>  #include <linux/io.h>
>  #include <linux/highmem.h>
>  #include <linux/wait.h>
> @@ -450,7 +451,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
>  	INIT_LIST_HEAD(&fbo->lru);
>  	INIT_LIST_HEAD(&fbo->swap);
>  	INIT_LIST_HEAD(&fbo->io_reserve_lru);
> -	fbo->vm_node = NULL;
> +	drm_vma_node_reset(&fbo->vma_node);
>  	atomic_set(&fbo->cpu_writers, 0);
>  
>  	spin_lock(&bdev->fence_lock);
> diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
> index 3df9f16..8c0e2c0 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
> @@ -33,6 +33,7 @@
>  #include <ttm/ttm_module.h>
>  #include <ttm/ttm_bo_driver.h>
>  #include <ttm/ttm_placement.h>
> +#include <drm/drm_vma_manager.h>
>  #include <linux/mm.h>
>  #include <linux/rbtree.h>
>  #include <linux/module.h>
> @@ -40,37 +41,6 @@
>  
>  #define TTM_BO_VM_NUM_PREFAULT 16
>  
> -static struct ttm_buffer_object *ttm_bo_vm_lookup_rb(struct ttm_bo_device *bdev,
> -						     unsigned long page_start,
> -						     unsigned long num_pages)
> -{
> -	struct rb_node *cur = bdev->addr_space_rb.rb_node;
> -	unsigned long cur_offset;
> -	struct ttm_buffer_object *bo;
> -	struct ttm_buffer_object *best_bo = NULL;
> -
> -	while (likely(cur != NULL)) {
> -		bo = rb_entry(cur, struct ttm_buffer_object, vm_rb);
> -		cur_offset = bo->vm_node->start;
> -		if (page_start >= cur_offset) {
> -			cur = cur->rb_right;
> -			best_bo = bo;
> -			if (page_start == cur_offset)
> -				break;
> -		} else
> -			cur = cur->rb_left;
> -	}
> -
> -	if (unlikely(best_bo == NULL))
> -		return NULL;
> -
> -	if (unlikely((best_bo->vm_node->start + best_bo->num_pages) <
> -		     (page_start + num_pages)))
> -		return NULL;
> -
> -	return best_bo;
> -}
> -
>  static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
>  {
>  	struct ttm_buffer_object *bo = (struct ttm_buffer_object *)
> @@ -146,9 +116,9 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
>  	}
>  
>  	page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) +
> -	    bo->vm_node->start - vma->vm_pgoff;
> +	    drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
>  	page_last = vma_pages(vma) +
> -	    bo->vm_node->start - vma->vm_pgoff;
> +	    drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
>  
>  	if (unlikely(page_offset >= bo->num_pages)) {
>  		retval = VM_FAULT_SIGBUS;
> @@ -249,6 +219,30 @@ static const struct vm_operations_struct ttm_bo_vm_ops = {
>  	.close = ttm_bo_vm_close
>  };
>  
> +static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device *bdev,
> +						  unsigned long offset,
> +						  unsigned long pages)
> +{
> +	struct drm_vma_offset_node *node;
> +	struct ttm_buffer_object *bo = NULL;
> +
> +	drm_vma_offset_lock_lookup(&bdev->vma_manager);
> +
> +	node = drm_vma_offset_lookup_locked(&bdev->vma_manager, offset, pages);
> +	if (likely(node)) {
> +		bo = container_of(node, struct ttm_buffer_object, vma_node);
> +		if (!kref_get_unless_zero(&bo->kref))
> +			bo = NULL;
> +	}
> +
> +	drm_vma_offset_unlock_lookup(&bdev->vma_manager);
> +
> +	if (!bo)
> +		pr_err("Could not find buffer object to map\n");
> +
> +	return bo;
> +}

Bikeshed for next time around: If you keep the function at the same spot
as the old one the diff looks a bit cuter&is nicer to read.

> +
>  int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
>  		struct ttm_bo_device *bdev)
>  {
> @@ -256,17 +250,9 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
>  	struct ttm_buffer_object *bo;
>  	int ret;
>  
> -	read_lock(&bdev->vm_lock);
> -	bo = ttm_bo_vm_lookup_rb(bdev, vma->vm_pgoff,
> -				 vma_pages(vma));
> -	if (likely(bo != NULL) && !kref_get_unless_zero(&bo->kref))
> -		bo = NULL;
> -	read_unlock(&bdev->vm_lock);
> -
> -	if (unlikely(bo == NULL)) {
> -		pr_err("Could not find buffer object to map\n");
> +	bo = ttm_bo_vm_lookup(bdev, vma->vm_pgoff, vma_pages(vma));
> +	if (unlikely(!bo))
>  		return -EINVAL;
> -	}
>  
>  	driver = bo->bdev->driver;
>  	if (unlikely(!driver->verify_access)) {
> @@ -324,12 +310,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
>  	bool no_wait = false;
>  	bool dummy;
>  
> -	read_lock(&bdev->vm_lock);
> -	bo = ttm_bo_vm_lookup_rb(bdev, dev_offset, 1);
> -	if (likely(bo != NULL))
> -		ttm_bo_reference(bo);
> -	read_unlock(&bdev->vm_lock);
> -
> +	bo = ttm_bo_vm_lookup(bdev, dev_offset, 1);
>  	if (unlikely(bo == NULL))
>  		return -EFAULT;
>  
> @@ -343,7 +324,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
>  	if (unlikely(ret != 0))
>  		goto out_unref;
>  
> -	kmap_offset = dev_offset - bo->vm_node->start;
> +	kmap_offset = dev_offset - drm_vma_node_start(&bo->vma_node);
>  	if (unlikely(kmap_offset >= bo->num_pages)) {
>  		ret = -EFBIG;
>  		goto out_unref;
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
> index 7953d1f..0e67cf4 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
> @@ -500,7 +500,7 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
>  		goto out_no_dmabuf;
>  
>  	rep->handle = handle;
> -	rep->map_handle = dma_buf->base.addr_space_offset;
> +	rep->map_handle = drm_vma_node_offset_addr(&dma_buf->base.vma_node);
>  	rep->cur_gmr_id = handle;
>  	rep->cur_gmr_offset = 0;
>  
> @@ -834,7 +834,7 @@ int vmw_dumb_map_offset(struct drm_file *file_priv,
>  	if (ret != 0)
>  		return -EINVAL;
>  
> -	*offset = out_buf->base.addr_space_offset;
> +	*offset = drm_vma_node_offset_addr(&out_buf->base.vma_node);
>  	vmw_dmabuf_unreference(&out_buf);
>  	return 0;
>  }
> diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
> index 8a6aa56..751eaff 100644
> --- a/include/drm/ttm/ttm_bo_api.h
> +++ b/include/drm/ttm/ttm_bo_api.h
> @@ -32,12 +32,12 @@
>  #define _TTM_BO_API_H_
>  
>  #include <drm/drm_hashtab.h>
> +#include <drm/drm_vma_manager.h>
>  #include <linux/kref.h>
>  #include <linux/list.h>
>  #include <linux/wait.h>
>  #include <linux/mutex.h>
>  #include <linux/mm.h>
> -#include <linux/rbtree.h>
>  #include <linux/bitmap.h>
>  #include <linux/reservation.h>
>  
> @@ -145,7 +145,6 @@ struct ttm_tt;
>   * @type: The bo type.
>   * @destroy: Destruction function. If NULL, kfree is used.
>   * @num_pages: Actual number of pages.
> - * @addr_space_offset: Address space offset.
>   * @acc_size: Accounted size for this object.
>   * @kref: Reference count of this buffer object. When this refcount reaches
>   * zero, the object is put on the delayed delete list.
> @@ -166,8 +165,7 @@ struct ttm_tt;
>   * @swap: List head for swap LRU list.
>   * @sync_obj: Pointer to a synchronization object.
>   * @priv_flags: Flags describing buffer object internal state.
> - * @vm_rb: Rb node for the vm rb tree.
> - * @vm_node: Address space manager node.
> + * @vma_node: Address space manager node.
>   * @offset: The current GPU offset, which can have different meanings
>   * depending on the memory type. For SYSTEM type memory, it should be 0.
>   * @cur_placement: Hint of current placement.
> @@ -194,7 +192,6 @@ struct ttm_buffer_object {
>  	enum ttm_bo_type type;
>  	void (*destroy) (struct ttm_buffer_object *);
>  	unsigned long num_pages;
> -	uint64_t addr_space_offset;
>  	size_t acc_size;
>  
>  	/**
> @@ -238,13 +235,7 @@ struct ttm_buffer_object {
>  	void *sync_obj;
>  	unsigned long priv_flags;
>  
> -	/**
> -	 * Members protected by the bdev::vm_lock
> -	 */
> -
> -	struct rb_node vm_rb;
> -	struct drm_mm_node *vm_node;
> -
> +	struct drm_vma_offset_node vma_node;
>  
>  	/**
>  	 * Special members that are protected by the reserve lock
> diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
> index 984fc2d..8639c85 100644
> --- a/include/drm/ttm/ttm_bo_driver.h
> +++ b/include/drm/ttm/ttm_bo_driver.h
> @@ -36,6 +36,7 @@
>  #include <ttm/ttm_placement.h>
>  #include <drm/drm_mm.h>
>  #include <drm/drm_global.h>
> +#include <drm/drm_vma_manager.h>
>  #include <linux/workqueue.h>
>  #include <linux/fs.h>
>  #include <linux/spinlock.h>
> @@ -519,7 +520,7 @@ struct ttm_bo_global {
>   * @man: An array of mem_type_managers.
>   * @fence_lock: Protects the synchronizing members on *all* bos belonging
>   * to this device.
> - * @addr_space_mm: Range manager for the device address space.
> + * @vma_manager: Address space manager
>   * lru_lock: Spinlock that protects the buffer+device lru lists and
>   * ddestroy lists.
>   * @val_seq: Current validation sequence.
> @@ -537,14 +538,13 @@ struct ttm_bo_device {
>  	struct list_head device_list;
>  	struct ttm_bo_global *glob;
>  	struct ttm_bo_driver *driver;
> -	rwlock_t vm_lock;
>  	struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES];
>  	spinlock_t fence_lock;
> +
>  	/*
> -	 * Protected by the vm lock.
> +	 * Protected by internal locks.
>  	 */
> -	struct rb_root addr_space_rb;
> -	struct drm_mm addr_space_mm;
> +	struct drm_vma_offset_manager vma_manager;
>  
>  	/*
>  	 * Protected by the global:lru lock.

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v4 4/4] drm/vma: provide drm_vma_node_unmap() helper
  2013-07-23 12:47   ` [PATCH v4 4/4] drm/vma: provide drm_vma_node_unmap() helper David Herrmann
@ 2013-07-24 15:58     ` Daniel Vetter
  2013-07-24 19:10     ` [PATCH v5 " David Herrmann
  1 sibling, 0 replies; 31+ messages in thread
From: Daniel Vetter @ 2013-07-24 15:58 UTC (permalink / raw)
  To: David Herrmann; +Cc: Dave Airlie, Thomas Hellstrom, dri-devel, Daniel Vetter

On Tue, Jul 23, 2013 at 02:47:16PM +0200, David Herrmann wrote:
> Instead of unmapping the nodes in TTM and GEM users manually, we provide
> a generic wrapper which does the correct thing for all vma-nodes.
> 
> v2: remove bdev->dev_mapping test in ttm_bo_unmap_virtual_unlocked() as
> ttm_mem_io_free_vm() does nothing in that case (io_reserved_vm is 0).
> v4: Fix docbook comments
> 
> Cc: Dave Airlie <airlied@redhat.com>
> Cc: Maarten Lankhorst <maarten.lankhorst@canonical.com>
> Cc: Thomas Hellstrom <thellstrom@vmware.com>
> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> ---
>  drivers/gpu/drm/i915/i915_gem.c |  6 +-----
>  drivers/gpu/drm/ttm/ttm_bo.c    | 11 +----------
>  include/drm/drm_vma_manager.h   | 22 ++++++++++++++++++++++
>  3 files changed, 24 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 53f81b3..8673a00 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -1427,11 +1427,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
>  	if (!obj->fault_mappable)
>  		return;
>  
> -	if (obj->base.dev->dev_mapping)
> -		unmap_mapping_range(obj->base.dev->dev_mapping,
> -				    (loff_t)drm_vma_node_offset_addr(&obj->base.vma_node),
> -				    obj->base.size, 1);
> -
> +	drm_vma_node_unmap(&obj->base.vma_node, obj->base.dev->dev_mapping);
>  	obj->fault_mappable = false;
>  }
>  
> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
> index 3dc08b6..050edfa 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> @@ -1488,17 +1488,8 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
>  void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo)
>  {
>  	struct ttm_bo_device *bdev = bo->bdev;
> -	loff_t offset, holelen;
>  
> -	if (!bdev->dev_mapping)
> -		return;
> -
> -	if (drm_vma_node_has_offset(&bo->vma_node)) {
> -		offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node);
> -		holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
> -
> -		unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
> -	}
> +	drm_vma_node_unmap(&bo->vma_node, bdev->dev_mapping);
>  	ttm_mem_io_free_vm(bo);
>  }
>  
> diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h
> index ace2925..2772192 100644
> --- a/include/drm/drm_vma_manager.h
> +++ b/include/drm/drm_vma_manager.h
> @@ -24,6 +24,7 @@
>   */
>  
>  #include <drm/drm_mm.h>
> +#include <linux/mm.h>
>  #include <linux/module.h>
>  #include <linux/rbtree.h>
>  #include <linux/spinlock.h>
> @@ -177,4 +178,25 @@ static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
>  	return ((__u64)node->vm_node.start) << PAGE_SHIFT;
>  }
>  
> +/**
> + * drm_vma_node_unmap() - Unmap offset node
> + * @node: Offset node
> + * @file_mapping: Address space to unmap @node from
> + *
> + * Unmap all userspace mappings for a given offset node. The mappings must be
> + * associated with the @file_mapping address-space. If no offset exists or
> + * the address-space is invalid, nothing is done.
> + *
> + * This call is unlocked. The caller must guarantee that drm_vma_offset_remove()
> + * is not called on this node concurrently.
> + */
> +static inline void drm_vma_node_unmap(struct drm_vma_offset_node *node,
> +				      struct address_space *file_mapping)
> +{
> +	if (file_mapping && drm_vma_node_has_offset(node))
> +		unmap_mapping_range(file_mapping,
> +				    drm_vma_node_offset_addr(node),
> +				    node->vm_pages << PAGE_SHIFT, 1);
> +}
> +
>  #endif /* __DRM_VMA_MANAGER_H__ */

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v4 1/4] drm: add unified vma offset manager
  2013-07-24 15:35     ` Daniel Vetter
@ 2013-07-24 16:10       ` David Herrmann
  0 siblings, 0 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-24 16:10 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Dave Airlie, dri-devel, Daniel Vetter

Hi

On Wed, Jul 24, 2013 at 5:35 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Tue, Jul 23, 2013 at 02:47:13PM +0200, David Herrmann wrote:
>> If we want to map GPU memory into user-space, we need to linearize the
>> addresses to not confuse mm-core. Currently, GEM and TTM both implement
>> their own offset-managers to assign a pgoff to each object for user-space
>> CPU access. GEM uses a hash-table, TTM uses an rbtree.
>>
>> This patch provides a unified implementation that can be used to replace
>> both. TTM allows partial mmaps with a given offset, so we cannot use
>> hashtables as the start address may not be known at mmap time. Hence, we
>> use the rbtree-implementation of TTM.
>>
>> We could easily update drm_mm to use an rbtree instead of a linked list
>> for it's object list and thus drop the rbtree from the vma-manager.
>> However, this would slow down drm_mm object allocation for all other
>> use-cases (rbtree insertion) and add another 4-8 bytes to each mm node.
>> Hence, use the separate tree but allow for later migration.
>>
>> This is a rewrite of the 2012-proposal by David Airlie <airlied@linux.ie>
>>
>> v2:
>>  - fix Docbook integration
>>  - drop drm_mm_node_linked() and use drm_mm_node_allocated()
>>  - remove unjustified likely/unlikely usage (but keep for rbtree paths)
>>  - remove BUG_ON() as drm_mm already does that
>>  - clarify page-based vs. byte-based addresses
>>  - use drm_vma_node_reset() for initialization, too
>> v4:
>>  - allow external locking via drm_vma_offset_un/lock_lookup()
>>  - add locked lookup helper drm_vma_offset_lookup_locked()
>>
>> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
>> ---
>>  Documentation/DocBook/drm.tmpl    |   6 +
>>  drivers/gpu/drm/Makefile          |   2 +-
>>  drivers/gpu/drm/drm_vma_manager.c | 283 ++++++++++++++++++++++++++++++++++++++
>>  include/drm/drm_vma_manager.h     | 180 ++++++++++++++++++++++++
>>  4 files changed, 470 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/gpu/drm/drm_vma_manager.c
>>  create mode 100644 include/drm/drm_vma_manager.h
>>
>> diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl
>> index 7d1278e..87e22ec 100644
>> --- a/Documentation/DocBook/drm.tmpl
>> +++ b/Documentation/DocBook/drm.tmpl
>> @@ -2212,6 +2212,12 @@ void intel_crt_init(struct drm_device *dev)
>>  !Iinclude/drm/drm_rect.h
>>  !Edrivers/gpu/drm/drm_rect.c
>>      </sect2>
>> +    <sect2>
>> +      <title>VMA Offset Manager</title>
>> +!Pdrivers/gpu/drm/drm_vma_manager.c vma offset manager
>> +!Edrivers/gpu/drm/drm_vma_manager.c
>> +!Iinclude/drm/drm_vma_manager.h
>> +    </sect2>
>>    </sect1>
>>
>>    <!-- Internals: kms properties -->
>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>> index 801bcaf..d943b94 100644
>> --- a/drivers/gpu/drm/Makefile
>> +++ b/drivers/gpu/drm/Makefile
>> @@ -13,7 +13,7 @@ drm-y       :=      drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
>>               drm_crtc.o drm_modes.o drm_edid.o \
>>               drm_info.o drm_debugfs.o drm_encoder_slave.o \
>>               drm_trace_points.o drm_global.o drm_prime.o \
>> -             drm_rect.o
>> +             drm_rect.o drm_vma_manager.o
>>
>>  drm-$(CONFIG_COMPAT) += drm_ioc32.o
>>  drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
>> diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c
>> new file mode 100644
>> index 0000000..27760bd
>> --- /dev/null
>> +++ b/drivers/gpu/drm/drm_vma_manager.c
>> @@ -0,0 +1,283 @@
>> +/*
>> + * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
>> + * Copyright (c) 2012 David Airlie <airlied@linux.ie>
>> + * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a
>> + * copy of this software and associated documentation files (the "Software"),
>> + * to deal in the Software without restriction, including without limitation
>> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
>> + * and/or sell copies of the Software, and to permit persons to whom the
>> + * Software is furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
>> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
>> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
>> + * OTHER DEALINGS IN THE SOFTWARE.
>> + */
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_mm.h>
>> +#include <drm/drm_vma_manager.h>
>> +#include <linux/mm.h>
>> +#include <linux/module.h>
>> +#include <linux/rbtree.h>
>> +#include <linux/slab.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/types.h>
>> +
>> +/**
>> + * DOC: vma offset manager
>> + *
>> + * The vma-manager is responsible to map arbitrary driver-dependent memory
>> + * regions into the linear user address-space. It provides offsets to the
>> + * caller which can then be used on the address_space of the drm-device. It
>> + * takes care to not overlap regions, size them appropriately and to not
>> + * confuse mm-core by inconsistent fake vm_pgoff fields.
>> + * Drivers shouldn't use this for object placement in VMEM. This manager should
>> + * only be used to manage mappings into linear user-space VMs.
>> + *
>> + * We use drm_mm as backend to manage object allocations. But it is highly
>> + * optimized for alloc/free calls, not lookups. Hence, we use an rb-tree to
>> + * speed up offset lookups.
>> + *
>> + * You must not use multiple offset managers on a single address_space.
>> + * Otherwise, mm-core will be unable to tear down memory mappings as the VM will
>> + * no longer be linear. Please use VM_NONLINEAR in that case and implement your
>> + * own offset managers.
>> + *
>> + * This offset manager works on page-based addresses. That is, every argument
>> + * and return code (with the exception of drm_vma_node_offset_addr()) is given
>> + * in number of pages, not number of bytes. That means, object sizes and offsets
>> + * must always be page-aligned (as usual).
>> + * If you want to get a valid byte-based user-space address for a given offset,
>> + * please see drm_vma_node_offset_addr().
>> + */
>> +
>> +/**
>> + * drm_vma_offset_manager_init - Initialize new offset-manager
>> + * @mgr: Manager object
>> + * @page_offset: Offset of available memory area (page-based)
>> + * @size: Size of available memory area (page-based)
>
> Bikeshed on naming: s/memory area/address space range/. Feel free to
> ignore.

Makes sense, yepp.

>> + *
>> + * Initialize a new offset-manager. The offset and area size available for the
>> + * manager are given as @page_offset and @size. Both are interpreted as
>> + * page-numbers, not bytes.
>> + *
>> + * Adding/removing nodes from the manager is locked internally and protected
>> + * against concurrent access. However, node allocation and destruction is left
>> + * for the caller. While calling into the vma-manager, a given node must
>> + * always be guaranteed to be referenced.
>> + */
>> +void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
>> +                              unsigned long page_offset, unsigned long size)
>> +{
>> +     rwlock_init(&mgr->vm_lock);
>> +     mgr->vm_addr_space_rb = RB_ROOT;
>> +     drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size);
>> +}
>> +EXPORT_SYMBOL(drm_vma_offset_manager_init);
>> +
>> +/**
>> + * drm_vma_offset_manager_destroy() - Destroy offset manager
>> + * @mgr: Manager object
>> + *
>> + * Destroy an object manager which was previously created via
>> + * drm_vma_offset_manager_init(). The caller must remove all allocated nodes
>> + * before destroying the manager. Otherwise, drm_mm will refuse to free the
>> + * requested resources.
>> + *
>> + * The manager must not be accessed after this function is called.
>> + */
>> +void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr)
>> +{
>> +     /* take the lock to protect against buggy drivers */
>> +     write_lock(&mgr->vm_lock);
>> +     drm_mm_takedown(&mgr->vm_addr_space_mm);
>> +     write_unlock(&mgr->vm_lock);
>> +}
>> +EXPORT_SYMBOL(drm_vma_offset_manager_destroy);
>> +
>> +/**
>> + * drm_vma_offset_lookup() - Find node in offset space
>> + * @mgr: Manager object
>> + * @start: Start address for object (page-based)
>> + * @pages: Size of object (page-based)
>> + *
>> + * Find a node given a start address and object size. This returns the _best_
>> + * match for the given node. That is, @start may point somewhere into a valid
>> + * region and the given node will be returned, as long as the node spans the
>> + * whole requested area (given the size in number of pages as @pages).
>> + *
>> + * RETURNS:
>> + * Returns NULL if no suitable node can be found. Otherwise, the best match
>> + * is returned. It's the caller's responsibility to make sure the node doesn't
>> + * get destroyed before the caller can access it.
>> + */
>> +struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr,
>> +                                               unsigned long start,
>> +                                               unsigned long pages)
>> +{
>> +     struct drm_vma_offset_node *node;
>> +
>> +     read_lock(&mgr->vm_lock);
>> +     node = drm_vma_offset_lookup_locked(mgr, start, pages);
>> +     read_unlock(&mgr->vm_lock);
>> +
>> +     return node;
>> +}
>> +EXPORT_SYMBOL(drm_vma_offset_lookup);
>> +
>> +/**
>> + * drm_vma_offset_lookup_locked() - Find node in offset space
>> + * @mgr: Manager object
>> + * @start: Start address for object (page-based)
>> + * @pages: Size of object (page-based)
>> + *
>> + * Same as drm_vma_offset_lookup() but requires the caller to lock offset lookup
>> + * manually. See drm_vma_offset_lock_lookup() for an example.
>> + *
>> + * RETURNS:
>> + * Returns NULL if no suitable node can be found. Otherwise, the best match
>> + * is returned.
>> + */
>> +struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
>> +                                                      unsigned long start,
>> +                                                      unsigned long pages)
>> +{
>> +     struct drm_vma_offset_node *node, *best;
>> +     struct rb_node *iter;
>> +     unsigned long offset;
>> +
>> +     iter = mgr->vm_addr_space_rb.rb_node;
>> +     best = NULL;
>> +
>> +     while (likely(iter)) {
>> +             node = rb_entry(iter, struct drm_vma_offset_node, vm_rb);
>> +             offset = node->vm_node.start;
>> +             if (start >= offset) {
>> +                     iter = iter->rb_right;
>> +                     best = node;
>> +                     if (start == offset)
>> +                             break;
>> +             } else {
>> +                     iter = iter->rb_left;
>> +             }
>> +     }
>> +
>> +     /* verify that the node spans the requested area */
>> +     if (best) {
>> +             offset = best->vm_node.start + best->vm_pages;
>> +             if (offset > start + pages)
>> +                     best = NULL;
>> +     }
>> +
>> +     return best;
>> +}
>> +EXPORT_SYMBOL(drm_vma_offset_lookup_locked);
>> +
>> +/* internal helper to link @node into the rb-tree */
>> +static void _drm_vma_offset_add_rb(struct drm_vma_offset_manager *mgr,
>> +                                struct drm_vma_offset_node *node)
>> +{
>> +     struct rb_node **iter = &mgr->vm_addr_space_rb.rb_node;
>> +     struct rb_node *parent = NULL;
>> +     struct drm_vma_offset_node *iter_node;
>> +
>> +     while (likely(*iter)) {
>> +             parent = *iter;
>> +             iter_node = rb_entry(*iter, struct drm_vma_offset_node, vm_rb);
>> +
>> +             if (node->vm_node.start < iter_node->vm_node.start)
>> +                     iter = &(*iter)->rb_left;
>> +             else if (node->vm_node.start > iter_node->vm_node.start)
>> +                     iter = &(*iter)->rb_right;
>> +             else
>> +                     BUG();
>> +     }
>> +
>> +     rb_link_node(&node->vm_rb, parent, iter);
>> +     rb_insert_color(&node->vm_rb, &mgr->vm_addr_space_rb);
>> +}
>> +
>> +/**
>> + * drm_vma_offset_add() - Add offset node to manager
>> + * @mgr: Manager object
>> + * @node: Node to be added
>> + * @pages: Allocation size visible to user-space (in number of pages)
>> + *
>> + * Add a node to the offset-manager. If the node was already added, this does
>> + * nothing and return 0. @pages is the size of the object given in number of
>> + * pages.
>> + * After this call succeeds, you can access the offset of the node until it
>> + * is removed again.
>> + *
>> + * If this call fails, it is safe to retry the operation or call
>> + * drm_vma_offset_remove(), anyway. However, no cleanup is required in that
>> + * case.
>> + *
>> + * @pages is not required to be the same size as the underlying memory object
>> + * that you want to map. It only limits the size that user-space can map into
>> + * their address space.
>> + *
>> + * RETURNS:
>> + * 0 on success, negative error code on failure.
>> + */
>> +int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
>> +                    struct drm_vma_offset_node *node, unsigned long pages)
>> +{
>> +     int ret;
>> +
>> +     write_lock(&mgr->vm_lock);
>> +
>> +     if (drm_mm_node_allocated(&node->vm_node)) {
>> +             ret = 0;
>> +             goto out_unlock;
>> +     }
>> +
>> +     ret = drm_mm_insert_node_generic(&mgr->vm_addr_space_mm,
>> +                                      &node->vm_node, pages, 0, 0);
>> +     if (ret)
>> +             goto out_unlock;
>> +
>> +     node->vm_pages = pages;
>
> Why not just use node->vm_node.size and remove vm_pages?

In the first draft I initialized the size directly. I added it to
drm_vma_offset_add() later, so that's a left-over. I will remove the
duplicate and use node->vm_node.size directly.

>> +     _drm_vma_offset_add_rb(mgr, node);
>> +     goto out_unlock;
>
> Unnecessary goto.

Fixed.

>> +
>> +out_unlock:
>> +     write_unlock(&mgr->vm_lock);
>> +     return ret;
>> +}
>> +EXPORT_SYMBOL(drm_vma_offset_add);
>> +
>> +/**
>> + * drm_vma_offset_remove() - Remove offset node from manager
>> + * @mgr: Manager object
>> + * @node: Node to be removed
>> + *
>> + * Remove a node from the offset manager. If the node wasn't added before, this
>> + * does nothing. After this call returns, the offset of the node must be not
>> + * accessed, anymore.
>
> I think we could clarify this a little "After this call returns the offset
> node must not be used until a new offset is allocatd with
> drm_vma_offset_add() again. Helper functions like
> drm_vma_node_offset_addr() will return invalid data"

They guarantee to return 0. I use that in the debugfs paths so we
don't have to check drm_vma_node_has_offset() all the time. So I guess
I will change it to "return 0 (invalid)" or alike instead of just
"invalid".

>> + * It is safe to add the node via drm_vma_offset_add() again.
>> + */
>> +void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
>> +                        struct drm_vma_offset_node *node)
>> +{
>> +     write_lock(&mgr->vm_lock);
>> +
>> +     if (drm_mm_node_allocated(&node->vm_node)) {
>> +             rb_erase(&node->vm_rb, &mgr->vm_addr_space_rb);
>> +             drm_mm_remove_node(&node->vm_node);
>> +             memset(&node->vm_node, 0, sizeof(node->vm_node));
>> +             node->vm_pages = 0;
>> +     }
>> +
>> +     write_unlock(&mgr->vm_lock);
>> +}
>> +EXPORT_SYMBOL(drm_vma_offset_remove);
>> diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h
>> new file mode 100644
>> index 0000000..ace2925
>> --- /dev/null
>> +++ b/include/drm/drm_vma_manager.h
>> @@ -0,0 +1,180 @@
>> +#ifndef __DRM_VMA_MANAGER_H__
>> +#define __DRM_VMA_MANAGER_H__
>> +
>> +/*
>> + * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a
>> + * copy of this software and associated documentation files (the "Software"),
>> + * to deal in the Software without restriction, including without limitation
>> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
>> + * and/or sell copies of the Software, and to permit persons to whom the
>> + * Software is furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
>> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
>> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
>> + * OTHER DEALINGS IN THE SOFTWARE.
>> + */
>> +
>> +#include <drm/drm_mm.h>
>> +#include <linux/module.h>
>> +#include <linux/rbtree.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/types.h>
>> +
>> +struct drm_vma_offset_node {
>> +     struct drm_mm_node vm_node;
>> +     struct rb_node vm_rb;
>> +     unsigned long vm_pages;
>> +};
>> +
>> +struct drm_vma_offset_manager {
>> +     rwlock_t vm_lock;
>> +     struct rb_root vm_addr_space_rb;
>> +     struct drm_mm vm_addr_space_mm;
>> +};
>> +
>> +void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
>> +                              unsigned long page_offset, unsigned long size);
>> +void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr);
>> +
>> +struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr,
>> +                                               unsigned long start,
>> +                                               unsigned long pages);
>> +struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
>> +                                                        unsigned long start,
>> +                                                        unsigned long pages);
>> +int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
>> +                    struct drm_vma_offset_node *node, unsigned long pages);
>> +void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
>> +                        struct drm_vma_offset_node *node);
>> +
>> +/**
>> + * drm_vma_offset_exact_lookup() - Look up node by exact address
>> + * @mgr: Manager object
>> + * @start: Start address (page-based, not byte-based)
>> + *
>> + * Same as drm_vma_offset_lookup() but does not allow any offset into the node.
>> + * It only returns the exact object with the given start address.
>> + *
>> + * RETURNS:
>> + * Node at exact start address @start.
>> + */
>> +static inline struct drm_vma_offset_node *
>> +drm_vma_offset_exact_lookup(struct drm_vma_offset_manager *mgr,
>> +                         unsigned long start)
>> +{
>> +     return drm_vma_offset_lookup(mgr, start, 1);
>> +}
>> +
>> +/**
>> + * drm_vma_offset_lock_lookup() - Lock lookup for extended private use
>> + * @mgr: Manager object
>> + *
>> + * Lock VMA manager for extended lookups. Only *_locked() VMA function calls
>> + * are allowed while holding this lock. All other contexts are blocked from VMA
>> + * until the lock is released via drm_vma_offset_unlock_lookup().
>> + *
>> + * Use this if you need to take a reference to the objects returned by
>> + * drm_vma_offset_lookup_locked() before releasing this lock again.
>> + *
>> + * This lock must not be used for anything else than extended lookups. You must
>> + * not call any other VMA helpers while holding this lock.
>> + *
>> + * Note: You're in atomic-context while holding this lock!
>> + *
>> + * Example:
>> + *   drm_vma_offset_lock_lookup(mgr);
>> + *   node = drm_vma_offset_lookup_locked(mgr);
>> + *   if (node)
>> + *       kref_get_unless_zero(container_of(node, sth, entr));
>> + *   drm_vma_offset_unlock_lookup(mgr);
>> + */
>> +static inline void drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr)
>> +{
>> +     read_lock(&mgr->vm_lock);
>> +}
>> +
>> +/**
>> + * drm_vma_offset_unlock_lookup() - Unlock lookup for extended private use
>> + * @mgr: Manager object
>> + *
>> + * Release lookup-lock. See drm_vma_offset_lock_lookup() for more information.
>> + */
>> +static inline void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr)
>> +{
>> +     read_unlock(&mgr->vm_lock);
>> +}
>> +
>> +/**
>> + * drm_vma_node_reset() - Initialize or reset node object
>> + * @node: Node to initialize or reset
>> + *
>> + * Reset a node to its initial state. You must call this before using @node with
>> + * any vma offset manager.
>
> The must here feels a bit strong since this is just a memset and pretty
> much everyone allocates with kzalloc. So calling this is redundant. Maybe
>
> "This must be called if @node isn't already cleared (e.g. by allocating
> with kzalloc) before it using it with any vma offset manager."

Makes sense. drm_mm has the same semantics, anyway.

>> + *
>> + * This must not be called on an already allocated node, or you will leak
>> + * memory.
>> + */
>> +static inline void drm_vma_node_reset(struct drm_vma_offset_node *node)
>> +{
>> +     memset(node, 0, sizeof(*node));
>> +}
>> +
>> +/**
>> + * drm_vma_node_start() - Return start address for page-based addressing
>> + * @node: Node to inspect
>> + *
>> + * Return the start address of the given node. This can be used as offset into
>> + * the linear VM space that is provided by the VMA offset manager. Note that
>> + * this can only be used for page-based addressing. If you need a proper offset
>> + * for user-space mappings, you must apply "<< PAGE_SHIFT" or use the
>> + * drm_vma_node_offset_addr() helper instead.
>> + *
>> + * RETURNS:
>> + * Start address of @node for page-based addressing. 0 if the node does not
>> + * have an offset allocated.
>> + */
>> +static inline unsigned long drm_vma_node_start(struct drm_vma_offset_node *node)
>> +{
>> +     return node->vm_node.start;
>> +}
>> +
>> +/**
>> + * drm_vma_node_has_offset() - Check whether node is added to offset manager
>> + * @node: Node to be checked
>> + *
>> + * RETURNS:
>> + * true iff the node was previously allocated an offset and added to
>> + * an vma offset manager.
>> + */
>> +static inline bool drm_vma_node_has_offset(struct drm_vma_offset_node *node)
>> +{
>> +     return drm_mm_node_allocated(&node->vm_node);
>> +}
>> +
>> +/**
>> + * drm_vma_node_offset_addr() - Return sanitized offset for user-space mmaps
>> + * @node: Linked offset node
>> + *
>> + * Same as drm_vma_node_start() but returns the address as a valid offset that
>> + * can be used for user-space mappings during mmap().
>> + * This must not be called on unlinked nodes.
>> + *
>> + * RETURNS:
>> + * Offset of @node for byte-based addressing. 0 if the node does not have an
>> + * object allocated.
>> + */
>> +static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
>> +{
>> +     return ((__u64)node->vm_node.start) << PAGE_SHIFT;
>> +}
>> +
>> +#endif /* __DRM_VMA_MANAGER_H__ */
>
> With the above tiny bikesheds addressed this is:
>
> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

Thanks
David

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v4 2/4] drm/gem: convert to new unified vma manager
  2013-07-24 15:52     ` Daniel Vetter
@ 2013-07-24 16:27       ` David Herrmann
  0 siblings, 0 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-24 16:27 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Daniel Vetter, dri-devel, Dave Airlie

Hi

On Wed, Jul 24, 2013 at 5:52 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Tue, Jul 23, 2013 at 02:47:14PM +0200, David Herrmann wrote:
>> Use the new vma manager instead of the old hashtable. Also convert all
>> drivers to use the new convenience helpers. This drops all the
>> (map_list.hash.key << PAGE_SHIFT) non-sense.
>>
>> Locking and access-management is exactly the same as before with an
>> additional lock inside of the vma-manager, which strictly wouldn't be
>> needed for gem.
>>
>> v2:
>>  - rebase on drm-next
>>  - init nodes via drm_vma_node_reset() in drm_gem.c
>> v3:
>>  - fix tegra
>> v4:
>>  - remove duplicate if (drm_vma_node_has_offset()) checks
>>  - inline now trivial drm_vma_node_offset_addr() calls
>>
>> Cc: Inki Dae <inki.dae@samsung.com>
>> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
>> Cc: Rob Clark <robdclark@gmail.com>
>> Cc: Dave Airlie <airlied@redhat.com>
>> Cc: Thierry Reding <thierry.reding@gmail.com>
>> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
>> Acked-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
>> ---
>>  drivers/gpu/drm/drm_gem.c                  | 89 +++++-------------------------
>>  drivers/gpu/drm/drm_gem_cma_helper.c       | 16 ++----
>>  drivers/gpu/drm/exynos/exynos_drm_gem.c    | 14 ++---
>>  drivers/gpu/drm/gma500/gem.c               | 15 ++---
>>  drivers/gpu/drm/i915/i915_gem.c            | 10 ++--
>>  drivers/gpu/drm/omapdrm/omap_gem.c         | 28 +++++-----
>>  drivers/gpu/drm/omapdrm/omap_gem_helpers.c | 49 +---------------
>>  drivers/gpu/drm/udl/udl_gem.c              | 13 ++---
>>  drivers/gpu/host1x/drm/gem.c               |  5 +-
>>  include/drm/drmP.h                         |  7 +--
>>  include/uapi/drm/drm.h                     |  2 +-
>>  11 files changed, 62 insertions(+), 186 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
>> index 1ad9e7e..8d3cdf3 100644
>> --- a/drivers/gpu/drm/drm_gem.c
>> +++ b/drivers/gpu/drm/drm_gem.c
>> @@ -37,6 +37,7 @@
>>  #include <linux/shmem_fs.h>
>>  #include <linux/dma-buf.h>
>>  #include <drm/drmP.h>
>> +#include <drm/drm_vma_manager.h>
>>
>>  /** @file drm_gem.c
>>   *
>> @@ -102,14 +103,9 @@ drm_gem_init(struct drm_device *dev)
>>       }
>>
>>       dev->mm_private = mm;
>> -
>> -     if (drm_ht_create(&mm->offset_hash, 12)) {
>> -             kfree(mm);
>> -             return -ENOMEM;
>> -     }
>> -
>> -     drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START,
>> -                 DRM_FILE_PAGE_OFFSET_SIZE);
>> +     drm_vma_offset_manager_init(&mm->vma_manager,
>> +                                 DRM_FILE_PAGE_OFFSET_START,
>> +                                 DRM_FILE_PAGE_OFFSET_SIZE);
>>
>>       return 0;
>>  }
>> @@ -119,8 +115,7 @@ drm_gem_destroy(struct drm_device *dev)
>>  {
>>       struct drm_gem_mm *mm = dev->mm_private;
>>
>> -     drm_mm_takedown(&mm->offset_manager);
>> -     drm_ht_remove(&mm->offset_hash);
>> +     drm_vma_offset_manager_destroy(&mm->vma_manager);
>>       kfree(mm);
>>       dev->mm_private = NULL;
>>  }
>> @@ -160,6 +155,7 @@ void drm_gem_private_object_init(struct drm_device *dev,
>>
>>       kref_init(&obj->refcount);
>>       atomic_set(&obj->handle_count, 0);
>> +     drm_vma_node_reset(&obj->vma_node);
>
> We already presume (but it's not really documented) that a gem object is
> cleared already (or allocated with kzalloc) so feels a bit like overhead.

I am fine with dropping it, if that's what gem already requires from drivers.

>>       obj->size = size;
>>  }
>>  EXPORT_SYMBOL(drm_gem_private_object_init);
>> @@ -302,12 +298,8 @@ drm_gem_free_mmap_offset(struct drm_gem_object *obj)
>>  {
>>       struct drm_device *dev = obj->dev;
>>       struct drm_gem_mm *mm = dev->mm_private;
>> -     struct drm_map_list *list = &obj->map_list;
>>
>> -     drm_ht_remove_item(&mm->offset_hash, &list->hash);
>> -     drm_mm_put_block(list->file_offset_node);
>> -     kfree(list->map);
>> -     list->map = NULL;
>> +     drm_vma_offset_remove(&mm->vma_manager, &obj->vma_node);
>>  }
>>  EXPORT_SYMBOL(drm_gem_free_mmap_offset);
>>
>> @@ -327,54 +319,9 @@ drm_gem_create_mmap_offset(struct drm_gem_object *obj)
>>  {
>>       struct drm_device *dev = obj->dev;
>>       struct drm_gem_mm *mm = dev->mm_private;
>> -     struct drm_map_list *list;
>> -     struct drm_local_map *map;
>> -     int ret;
>> -
>> -     /* Set the object up for mmap'ing */
>> -     list = &obj->map_list;
>> -     list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
>> -     if (!list->map)
>> -             return -ENOMEM;
>> -
>> -     map = list->map;
>> -     map->type = _DRM_GEM;
>> -     map->size = obj->size;
>> -     map->handle = obj;
>> -
>> -     /* Get a DRM GEM mmap offset allocated... */
>> -     list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
>> -                     obj->size / PAGE_SIZE, 0, false);
>> -
>> -     if (!list->file_offset_node) {
>> -             DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
>> -             ret = -ENOSPC;
>> -             goto out_free_list;
>> -     }
>>
>> -     list->file_offset_node = drm_mm_get_block(list->file_offset_node,
>> -                     obj->size / PAGE_SIZE, 0);
>> -     if (!list->file_offset_node) {
>> -             ret = -ENOMEM;
>> -             goto out_free_list;
>> -     }
>> -
>> -     list->hash.key = list->file_offset_node->start;
>> -     ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
>> -     if (ret) {
>> -             DRM_ERROR("failed to add to map hash\n");
>> -             goto out_free_mm;
>> -     }
>> -
>> -     return 0;
>> -
>> -out_free_mm:
>> -     drm_mm_put_block(list->file_offset_node);
>> -out_free_list:
>> -     kfree(list->map);
>> -     list->map = NULL;
>> -
>> -     return ret;
>> +     return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node,
>> +                               obj->size / PAGE_SIZE);
>>  }
>>  EXPORT_SYMBOL(drm_gem_create_mmap_offset);
>>
>> @@ -703,8 +650,8 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
>>       struct drm_file *priv = filp->private_data;
>>       struct drm_device *dev = priv->minor->dev;
>>       struct drm_gem_mm *mm = dev->mm_private;
>> -     struct drm_local_map *map = NULL;
>> -     struct drm_hash_item *hash;
>> +     struct drm_gem_object *obj;
>> +     struct drm_vma_offset_node *node;
>>       int ret = 0;
>>
>>       if (drm_device_is_unplugged(dev))
>> @@ -712,21 +659,15 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
>>
>>       mutex_lock(&dev->struct_mutex);
>>
>> -     if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
>> +     node = drm_vma_offset_exact_lookup(&mm->vma_manager, vma->vm_pgoff);
>> +     if (!node) {
>>               mutex_unlock(&dev->struct_mutex);
>>               return drm_mmap(filp, vma);
>>       }
>>
>> -     map = drm_hash_entry(hash, struct drm_map_list, hash)->map;
>> -     if (!map ||
>> -         ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) {
>> -             ret =  -EPERM;
>> -             goto out_unlock;
>> -     }
>> -
>> -     ret = drm_gem_mmap_obj(map->handle, map->size, vma);
>> +     obj = container_of(node, struct drm_gem_object, vma_node);
>> +     ret = drm_gem_mmap_obj(obj, node->vm_pages, vma);
>
> Iirc the old code only resulted in a ht hit if the start of the mmap
> regioni exactly matched the offset. The new code (I think at least, maybe
> I've misread it a bit) will return an object even if the start isn't
> aligned. drm_gem_mmap_obj checks already that the object is big enough but
> we don't check the start any more. I think this should be readded.
>
> It's a bit funny that gem wants an exact match but is ok with just mapping
> the beginning of the object, but I guess that abi is now enshrined ;-)

drm_vma_offset_exact_lookup() was meant for exactly that but it fails
to actually verify it. I can add a "drm_vma_node_start() == start"
check and return NULL if it doesn't match. The name is currently a bit
misleading.

I will fix that.

>>
>> -out_unlock:
>>       mutex_unlock(&dev->struct_mutex);
>>
>>       return ret;
>> diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
>> index ece72a8..bf09a6dc 100644
>> --- a/drivers/gpu/drm/drm_gem_cma_helper.c
>> +++ b/drivers/gpu/drm/drm_gem_cma_helper.c
>> @@ -27,11 +27,7 @@
>>  #include <drm/drmP.h>
>>  #include <drm/drm.h>
>>  #include <drm/drm_gem_cma_helper.h>
>> -
>> -static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
>> -{
>> -     return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
>> -}
>> +#include <drm/drm_vma_manager.h>
>>
>>  /*
>>   * __drm_gem_cma_create - Create a GEM CMA object without allocating memory
>> @@ -172,8 +168,7 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
>>  {
>>       struct drm_gem_cma_object *cma_obj;
>>
>> -     if (gem_obj->map_list.map)
>> -             drm_gem_free_mmap_offset(gem_obj);
>> +     drm_gem_free_mmap_offset(gem_obj);
>>
>>       cma_obj = to_drm_gem_cma_obj(gem_obj);
>>
>> @@ -237,7 +232,7 @@ int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
>>               return -EINVAL;
>>       }
>>
>> -     *offset = get_gem_mmap_offset(gem_obj);
>> +     *offset = (unsigned int)drm_vma_node_offset_addr(&gem_obj->vma_node);
>
> *offset is an u64, same as drm_vma_node_offset_addr. Why do we need a cast
> here?

Copied from the helper above. I can fix it.

>>
>>       drm_gem_object_unreference(gem_obj);
>>
>> @@ -301,12 +296,11 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m
>>  {
>>       struct drm_gem_object *obj = &cma_obj->base;
>>       struct drm_device *dev = obj->dev;
>> -     uint64_t off = 0;
>> +     uint64_t off;
>>
>>       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
>>
>> -     if (obj->map_list.map)
>> -             off = (uint64_t)obj->map_list.hash.key;
>> +     off = drm_vma_node_start(&obj->vma_node);
>>
>>       seq_printf(m, "%2d (%2d) %08llx %08Zx %p %d",
>>                       obj->name, obj->refcount.refcount.counter,
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
>> index 24c22a8..be32db1 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
>> @@ -10,6 +10,7 @@
>>   */
>>
>>  #include <drm/drmP.h>
>> +#include <drm/drm_vma_manager.h>
>>
>>  #include <linux/shmem_fs.h>
>>  #include <drm/exynos_drm.h>
>> @@ -152,8 +153,7 @@ out:
>>       exynos_drm_fini_buf(obj->dev, buf);
>>       exynos_gem_obj->buffer = NULL;
>>
>> -     if (obj->map_list.map)
>> -             drm_gem_free_mmap_offset(obj);
>> +     drm_gem_free_mmap_offset(obj);
>>
>>       /* release file pointer to gem object. */
>>       drm_gem_object_release(obj);
>> @@ -703,13 +703,11 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
>>               goto unlock;
>>       }
>>
>> -     if (!obj->map_list.map) {
>> -             ret = drm_gem_create_mmap_offset(obj);
>> -             if (ret)
>> -                     goto out;
>> -     }
>> +     ret = drm_gem_create_mmap_offset(obj);
>> +     if (ret)
>> +             goto out;
>>
>> -     *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
>> +     *offset = drm_vma_node_offset_addr(&obj->vma_node);
>>       DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
>>
>>  out:
>> diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c
>> index fe1d332..2f77bea 100644
>> --- a/drivers/gpu/drm/gma500/gem.c
>> +++ b/drivers/gpu/drm/gma500/gem.c
>> @@ -26,6 +26,7 @@
>>  #include <drm/drmP.h>
>>  #include <drm/drm.h>
>>  #include <drm/gma_drm.h>
>> +#include <drm/drm_vma_manager.h>
>>  #include "psb_drv.h"
>>
>>  int psb_gem_init_object(struct drm_gem_object *obj)
>> @@ -38,8 +39,7 @@ void psb_gem_free_object(struct drm_gem_object *obj)
>>       struct gtt_range *gtt = container_of(obj, struct gtt_range, gem);
>>
>>       /* Remove the list map if one is present */
>> -     if (obj->map_list.map)
>> -             drm_gem_free_mmap_offset(obj);
>> +     drm_gem_free_mmap_offset(obj);
>>       drm_gem_object_release(obj);
>>
>>       /* This must occur last as it frees up the memory of the GEM object */
>> @@ -81,13 +81,10 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev,
>>       /* What validation is needed here ? */
>>
>>       /* Make it mmapable */
>> -     if (!obj->map_list.map) {
>> -             ret = drm_gem_create_mmap_offset(obj);
>> -             if (ret)
>> -                     goto out;
>> -     }
>> -     /* GEM should really work out the hash offsets for us */
>> -     *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
>> +     ret = drm_gem_create_mmap_offset(obj);
>> +     if (ret)
>> +             goto out;
>> +     *offset = drm_vma_node_offset_addr(&obj->vma_node);
>>  out:
>>       drm_gem_object_unreference(obj);
>>  unlock:
>> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
>> index 46bf7e3..53f81b3 100644
>> --- a/drivers/gpu/drm/i915/i915_gem.c
>> +++ b/drivers/gpu/drm/i915/i915_gem.c
>> @@ -26,6 +26,7 @@
>>   */
>>
>>  #include <drm/drmP.h>
>> +#include <drm/drm_vma_manager.h>
>>  #include <drm/i915_drm.h>
>>  #include "i915_drv.h"
>>  #include "i915_trace.h"
>> @@ -1428,7 +1429,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
>>
>>       if (obj->base.dev->dev_mapping)
>>               unmap_mapping_range(obj->base.dev->dev_mapping,
>> -                                 (loff_t)obj->base.map_list.hash.key<<PAGE_SHIFT,
>> +                                 (loff_t)drm_vma_node_offset_addr(&obj->base.vma_node),
>>                                   obj->base.size, 1);
>>
>>       obj->fault_mappable = false;
>> @@ -1486,7 +1487,7 @@ static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj)
>>       struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
>>       int ret;
>>
>> -     if (obj->base.map_list.map)
>> +     if (drm_vma_node_has_offset(&obj->base.vma_node))
>>               return 0;
>>
>>       dev_priv->mm.shrinker_no_lock_stealing = true;
>> @@ -1517,9 +1518,6 @@ out:
>>
>>  static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj)
>>  {
>> -     if (!obj->base.map_list.map)
>> -             return;
>> -
>>       drm_gem_free_mmap_offset(&obj->base);
>>  }
>>
>> @@ -1558,7 +1556,7 @@ i915_gem_mmap_gtt(struct drm_file *file,
>>       if (ret)
>>               goto out;
>>
>> -     *offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT;
>> +     *offset = drm_vma_node_offset_addr(&obj->base.vma_node);
>>
>>  out:
>>       drm_gem_object_unreference(&obj->base);
>> diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
>> index cbcd71e..f90531fc00 100644
>> --- a/drivers/gpu/drm/omapdrm/omap_gem.c
>> +++ b/drivers/gpu/drm/omapdrm/omap_gem.c
>> @@ -20,6 +20,7 @@
>>
>>  #include <linux/spinlock.h>
>>  #include <linux/shmem_fs.h>
>> +#include <drm/drm_vma_manager.h>
>>
>>  #include "omap_drv.h"
>>  #include "omap_dmm_tiler.h"
>> @@ -308,21 +309,20 @@ uint32_t omap_gem_flags(struct drm_gem_object *obj)
>>  static uint64_t mmap_offset(struct drm_gem_object *obj)
>>  {
>>       struct drm_device *dev = obj->dev;
>> +     int ret;
>> +     size_t size;
>>
>>       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
>>
>> -     if (!obj->map_list.map) {
>> -             /* Make it mmapable */
>> -             size_t size = omap_gem_mmap_size(obj);
>> -             int ret = _drm_gem_create_mmap_offset_size(obj, size);
>> -
>> -             if (ret) {
>> -                     dev_err(dev->dev, "could not allocate mmap offset\n");
>> -                     return 0;
>> -             }
>> +     /* Make it mmapable */
>> +     size = omap_gem_mmap_size(obj);
>> +     ret = _drm_gem_create_mmap_offset_size(obj, size);
>> +     if (ret) {
>> +             dev_err(dev->dev, "could not allocate mmap offset\n");
>> +             return 0;
>>       }
>>
>> -     return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT;
>> +     return drm_vma_node_offset_addr(&obj->vma_node);
>>  }
>>
>>  uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
>> @@ -997,12 +997,11 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
>>  {
>>       struct drm_device *dev = obj->dev;
>>       struct omap_gem_object *omap_obj = to_omap_bo(obj);
>> -     uint64_t off = 0;
>> +     uint64_t off;
>>
>>       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
>>
>> -     if (obj->map_list.map)
>> -             off = (uint64_t)obj->map_list.hash.key;
>> +     off = drm_vma_node_start(&obj->vma_node);
>>
>>       seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d",
>>                       omap_obj->flags, obj->name, obj->refcount.refcount.counter,
>> @@ -1309,8 +1308,7 @@ void omap_gem_free_object(struct drm_gem_object *obj)
>>
>>       list_del(&omap_obj->mm_list);
>>
>> -     if (obj->map_list.map)
>> -             drm_gem_free_mmap_offset(obj);
>> +     drm_gem_free_mmap_offset(obj);
>>
>>       /* this means the object is still pinned.. which really should
>>        * not happen.  I think..
>> diff --git a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
>> index f9eb679..dbb1575 100644
>> --- a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
>> +++ b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
>> @@ -118,52 +118,7 @@ _drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size)
>>  {
>>       struct drm_device *dev = obj->dev;
>>       struct drm_gem_mm *mm = dev->mm_private;
>> -     struct drm_map_list *list;
>> -     struct drm_local_map *map;
>> -     int ret = 0;
>> -
>> -     /* Set the object up for mmap'ing */
>> -     list = &obj->map_list;
>> -     list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
>> -     if (!list->map)
>> -             return -ENOMEM;
>> -
>> -     map = list->map;
>> -     map->type = _DRM_GEM;
>> -     map->size = size;
>> -     map->handle = obj;
>> -
>> -     /* Get a DRM GEM mmap offset allocated... */
>> -     list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
>> -                     size / PAGE_SIZE, 0, 0);
>> -
>> -     if (!list->file_offset_node) {
>> -             DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
>> -             ret = -ENOSPC;
>> -             goto out_free_list;
>> -     }
>> -
>> -     list->file_offset_node = drm_mm_get_block(list->file_offset_node,
>> -                     size / PAGE_SIZE, 0);
>> -     if (!list->file_offset_node) {
>> -             ret = -ENOMEM;
>> -             goto out_free_list;
>> -     }
>> -
>> -     list->hash.key = list->file_offset_node->start;
>> -     ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
>> -     if (ret) {
>> -             DRM_ERROR("failed to add to map hash\n");
>> -             goto out_free_mm;
>> -     }
>> -
>> -     return 0;
>> -
>> -out_free_mm:
>> -     drm_mm_put_block(list->file_offset_node);
>> -out_free_list:
>> -     kfree(list->map);
>> -     list->map = NULL;
>>
>> -     return ret;
>> +     return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node,
>> +                               size / PAGE_SIZE);
>>  }
>> diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
>> index ef034fa..2a4cb2f 100644
>> --- a/drivers/gpu/drm/udl/udl_gem.c
>> +++ b/drivers/gpu/drm/udl/udl_gem.c
>> @@ -223,8 +223,7 @@ void udl_gem_free_object(struct drm_gem_object *gem_obj)
>>       if (obj->pages)
>>               udl_gem_put_pages(obj);
>>
>> -     if (gem_obj->map_list.map)
>> -             drm_gem_free_mmap_offset(gem_obj);
>> +     drm_gem_free_mmap_offset(gem_obj);
>>  }
>>
>>  /* the dumb interface doesn't work with the GEM straight MMAP
>> @@ -247,13 +246,11 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev,
>>       ret = udl_gem_get_pages(gobj, GFP_KERNEL);
>>       if (ret)
>>               goto out;
>> -     if (!gobj->base.map_list.map) {
>> -             ret = drm_gem_create_mmap_offset(obj);
>> -             if (ret)
>> -                     goto out;
>> -     }
>> +     ret = drm_gem_create_mmap_offset(obj);
>> +     if (ret)
>> +             goto out;
>>
>> -     *offset = (u64)gobj->base.map_list.hash.key << PAGE_SHIFT;
>> +     *offset = drm_vma_node_offset_addr(&gobj->base.vma_node);
>>
>>  out:
>>       drm_gem_object_unreference(&gobj->base);
>> diff --git a/drivers/gpu/host1x/drm/gem.c b/drivers/gpu/host1x/drm/gem.c
>> index c5e9a9b..bc323b3 100644
>> --- a/drivers/gpu/host1x/drm/gem.c
>> +++ b/drivers/gpu/host1x/drm/gem.c
>> @@ -108,7 +108,7 @@ static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo)
>>
>>  unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo)
>>  {
>> -     return (unsigned int)bo->gem.map_list.hash.key << PAGE_SHIFT;
>> +     return (unsigned int)drm_vma_node_offset_addr(&bo->gem.vma_node);
>
> Ok, tegra is pretty funny here about mmap offsets, using an int.
> Whatever, pre-existing fanciness so not our problem.

All TTM helpers use "unsigned int", too. We can fix that in a followup patch.

>>  }
>>
>>  struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
>> @@ -182,8 +182,7 @@ void tegra_bo_free_object(struct drm_gem_object *gem)
>>  {
>>       struct tegra_bo *bo = to_tegra_bo(gem);
>>
>> -     if (gem->map_list.map)
>> -             drm_gem_free_mmap_offset(gem);
>> +     drm_gem_free_mmap_offset(gem);
>>
>>       drm_gem_object_release(gem);
>>       tegra_bo_destroy(gem->dev, bo);
>> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
>> index 0ab6a09..4b518e0 100644
>> --- a/include/drm/drmP.h
>> +++ b/include/drm/drmP.h
>> @@ -71,6 +71,7 @@
>>  #include <asm/pgalloc.h>
>>  #include <drm/drm.h>
>>  #include <drm/drm_sarea.h>
>> +#include <drm/drm_vma_manager.h>
>>
>>  #include <linux/idr.h>
>>
>> @@ -587,7 +588,6 @@ struct drm_map_list {
>>       struct drm_local_map *map;      /**< mapping */
>>       uint64_t user_token;
>>       struct drm_master *master;
>> -     struct drm_mm_node *file_offset_node;   /**< fake offset */
>>  };
>>
>>  /**
>> @@ -622,8 +622,7 @@ struct drm_ati_pcigart_info {
>>   * GEM specific mm private for tracking GEM objects
>>   */
>>  struct drm_gem_mm {
>> -     struct drm_mm offset_manager;   /**< Offset mgmt for buffer objects */
>> -     struct drm_open_hash offset_hash; /**< User token hash table for maps */
>> +     struct drm_vma_offset_manager vma_manager;
>>  };
>>
>>  /**
>> @@ -644,7 +643,7 @@ struct drm_gem_object {
>>       struct file *filp;
>>
>>       /* Mapping info for this object */
>> -     struct drm_map_list map_list;
>> +     struct drm_vma_offset_node vma_node;
>>
>>       /**
>>        * Size of the object, in bytes.  Immutable over the object's
>> diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
>> index 238a166..272580c 100644
>> --- a/include/uapi/drm/drm.h
>> +++ b/include/uapi/drm/drm.h
>> @@ -181,7 +181,7 @@ enum drm_map_type {
>>       _DRM_AGP = 3,             /**< AGP/GART */
>>       _DRM_SCATTER_GATHER = 4,  /**< Scatter/gather memory for PCI DMA */
>>       _DRM_CONSISTENT = 5,      /**< Consistent memory for PCI DMA */
>> -     _DRM_GEM = 6,             /**< GEM object */
>> +     _DRM_GEM = 6,             /**< GEM object (obsolete) */
>>  };
>>
>>  /**
>> --
>> 1.8.3.3
>
> A few small things, with those addressed this is:
>
> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>
> I guess with the common infrastructure we'll have a bit of room for
> follow-up cleanups, but that can be done on especially rainy autumn days
> ;-)

Thanks!
David

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v5 1/4] drm: add unified vma offset manager
  2013-07-23 12:47   ` [PATCH v4 1/4] drm: add unified vma offset manager David Herrmann
  2013-07-24 15:35     ` Daniel Vetter
@ 2013-07-24 19:06     ` David Herrmann
  1 sibling, 0 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-24 19:06 UTC (permalink / raw)
  To: dri-devel; +Cc: Dave Airlie

If we want to map GPU memory into user-space, we need to linearize the
addresses to not confuse mm-core. Currently, GEM and TTM both implement
their own offset-managers to assign a pgoff to each object for user-space
CPU access. GEM uses a hash-table, TTM uses an rbtree.

This patch provides a unified implementation that can be used to replace
both. TTM allows partial mmaps with a given offset, so we cannot use
hashtables as the start address may not be known at mmap time. Hence, we
use the rbtree-implementation of TTM.

We could easily update drm_mm to use an rbtree instead of a linked list
for it's object list and thus drop the rbtree from the vma-manager.
However, this would slow down drm_mm object allocation for all other
use-cases (rbtree insertion) and add another 4-8 bytes to each mm node.
Hence, use the separate tree but allow for later migration.

This is a rewrite of the 2012-proposal by David Airlie <airlied@linux.ie>

v2:
 - fix Docbook integration
 - drop drm_mm_node_linked() and use drm_mm_node_allocated()
 - remove unjustified likely/unlikely usage (but keep for rbtree paths)
 - remove BUG_ON() as drm_mm already does that
 - clarify page-based vs. byte-based addresses
 - use drm_vma_node_reset() for initialization, too
v4:
 - allow external locking via drm_vma_offset_un/lock_lookup()
 - add locked lookup helper drm_vma_offset_lookup_locked()
v5:
 - fix drm_vma_offset_lookup() to correctly validate range-mismatches
   (fix (offset > start + pages))
 - fix drm_vma_offset_exact_lookup() to actually do what it says
 - remove redundant vm_pages member (add drm_vma_node_size() helper)
 - remove unneeded goto
 - fix documentation

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---
 Documentation/DocBook/drm.tmpl    |   6 +
 drivers/gpu/drm/Makefile          |   2 +-
 drivers/gpu/drm/drm_vma_manager.c | 281 ++++++++++++++++++++++++++++++++++++++
 include/drm/drm_vma_manager.h     | 202 +++++++++++++++++++++++++++
 4 files changed, 490 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/drm_vma_manager.c
 create mode 100644 include/drm/drm_vma_manager.h

diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl
index 7d1278e..87e22ec 100644
--- a/Documentation/DocBook/drm.tmpl
+++ b/Documentation/DocBook/drm.tmpl
@@ -2212,6 +2212,12 @@ void intel_crt_init(struct drm_device *dev)
 !Iinclude/drm/drm_rect.h
 !Edrivers/gpu/drm/drm_rect.c
     </sect2>
+    <sect2>
+      <title>VMA Offset Manager</title>
+!Pdrivers/gpu/drm/drm_vma_manager.c vma offset manager
+!Edrivers/gpu/drm/drm_vma_manager.c
+!Iinclude/drm/drm_vma_manager.h
+    </sect2>
   </sect1>
 
   <!-- Internals: kms properties -->
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 801bcaf..d943b94 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -13,7 +13,7 @@ drm-y       :=	drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
 		drm_crtc.o drm_modes.o drm_edid.o \
 		drm_info.o drm_debugfs.o drm_encoder_slave.o \
 		drm_trace_points.o drm_global.o drm_prime.o \
-		drm_rect.o
+		drm_rect.o drm_vma_manager.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c
new file mode 100644
index 0000000..b966cea
--- /dev/null
+++ b/drivers/gpu/drm/drm_vma_manager.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
+ * Copyright (c) 2012 David Airlie <airlied@linux.ie>
+ * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_mm.h>
+#include <drm/drm_vma_manager.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+/**
+ * DOC: vma offset manager
+ *
+ * The vma-manager is responsible to map arbitrary driver-dependent memory
+ * regions into the linear user address-space. It provides offsets to the
+ * caller which can then be used on the address_space of the drm-device. It
+ * takes care to not overlap regions, size them appropriately and to not
+ * confuse mm-core by inconsistent fake vm_pgoff fields.
+ * Drivers shouldn't use this for object placement in VMEM. This manager should
+ * only be used to manage mappings into linear user-space VMs.
+ *
+ * We use drm_mm as backend to manage object allocations. But it is highly
+ * optimized for alloc/free calls, not lookups. Hence, we use an rb-tree to
+ * speed up offset lookups.
+ *
+ * You must not use multiple offset managers on a single address_space.
+ * Otherwise, mm-core will be unable to tear down memory mappings as the VM will
+ * no longer be linear. Please use VM_NONLINEAR in that case and implement your
+ * own offset managers.
+ *
+ * This offset manager works on page-based addresses. That is, every argument
+ * and return code (with the exception of drm_vma_node_offset_addr()) is given
+ * in number of pages, not number of bytes. That means, object sizes and offsets
+ * must always be page-aligned (as usual).
+ * If you want to get a valid byte-based user-space address for a given offset,
+ * please see drm_vma_node_offset_addr().
+ */
+
+/**
+ * drm_vma_offset_manager_init - Initialize new offset-manager
+ * @mgr: Manager object
+ * @page_offset: Offset of available memory area (page-based)
+ * @size: Size of available address space range (page-based)
+ *
+ * Initialize a new offset-manager. The offset and area size available for the
+ * manager are given as @page_offset and @size. Both are interpreted as
+ * page-numbers, not bytes.
+ *
+ * Adding/removing nodes from the manager is locked internally and protected
+ * against concurrent access. However, node allocation and destruction is left
+ * for the caller. While calling into the vma-manager, a given node must
+ * always be guaranteed to be referenced.
+ */
+void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
+				 unsigned long page_offset, unsigned long size)
+{
+	rwlock_init(&mgr->vm_lock);
+	mgr->vm_addr_space_rb = RB_ROOT;
+	drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size);
+}
+EXPORT_SYMBOL(drm_vma_offset_manager_init);
+
+/**
+ * drm_vma_offset_manager_destroy() - Destroy offset manager
+ * @mgr: Manager object
+ *
+ * Destroy an object manager which was previously created via
+ * drm_vma_offset_manager_init(). The caller must remove all allocated nodes
+ * before destroying the manager. Otherwise, drm_mm will refuse to free the
+ * requested resources.
+ *
+ * The manager must not be accessed after this function is called.
+ */
+void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr)
+{
+	/* take the lock to protect against buggy drivers */
+	write_lock(&mgr->vm_lock);
+	drm_mm_takedown(&mgr->vm_addr_space_mm);
+	write_unlock(&mgr->vm_lock);
+}
+EXPORT_SYMBOL(drm_vma_offset_manager_destroy);
+
+/**
+ * drm_vma_offset_lookup() - Find node in offset space
+ * @mgr: Manager object
+ * @start: Start address for object (page-based)
+ * @pages: Size of object (page-based)
+ *
+ * Find a node given a start address and object size. This returns the _best_
+ * match for the given node. That is, @start may point somewhere into a valid
+ * region and the given node will be returned, as long as the node spans the
+ * whole requested area (given the size in number of pages as @pages).
+ *
+ * RETURNS:
+ * Returns NULL if no suitable node can be found. Otherwise, the best match
+ * is returned. It's the caller's responsibility to make sure the node doesn't
+ * get destroyed before the caller can access it.
+ */
+struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr,
+						  unsigned long start,
+						  unsigned long pages)
+{
+	struct drm_vma_offset_node *node;
+
+	read_lock(&mgr->vm_lock);
+	node = drm_vma_offset_lookup_locked(mgr, start, pages);
+	read_unlock(&mgr->vm_lock);
+
+	return node;
+}
+EXPORT_SYMBOL(drm_vma_offset_lookup);
+
+/**
+ * drm_vma_offset_lookup_locked() - Find node in offset space
+ * @mgr: Manager object
+ * @start: Start address for object (page-based)
+ * @pages: Size of object (page-based)
+ *
+ * Same as drm_vma_offset_lookup() but requires the caller to lock offset lookup
+ * manually. See drm_vma_offset_lock_lookup() for an example.
+ *
+ * RETURNS:
+ * Returns NULL if no suitable node can be found. Otherwise, the best match
+ * is returned.
+ */
+struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
+							 unsigned long start,
+							 unsigned long pages)
+{
+	struct drm_vma_offset_node *node, *best;
+	struct rb_node *iter;
+	unsigned long offset;
+
+	iter = mgr->vm_addr_space_rb.rb_node;
+	best = NULL;
+
+	while (likely(iter)) {
+		node = rb_entry(iter, struct drm_vma_offset_node, vm_rb);
+		offset = node->vm_node.start;
+		if (start >= offset) {
+			iter = iter->rb_right;
+			best = node;
+			if (start == offset)
+				break;
+		} else {
+			iter = iter->rb_left;
+		}
+	}
+
+	/* verify that the node spans the requested area */
+	if (best) {
+		offset = best->vm_node.start + best->vm_node.size;
+		if (offset < start + pages)
+			best = NULL;
+	}
+
+	return best;
+}
+EXPORT_SYMBOL(drm_vma_offset_lookup_locked);
+
+/* internal helper to link @node into the rb-tree */
+static void _drm_vma_offset_add_rb(struct drm_vma_offset_manager *mgr,
+				   struct drm_vma_offset_node *node)
+{
+	struct rb_node **iter = &mgr->vm_addr_space_rb.rb_node;
+	struct rb_node *parent = NULL;
+	struct drm_vma_offset_node *iter_node;
+
+	while (likely(*iter)) {
+		parent = *iter;
+		iter_node = rb_entry(*iter, struct drm_vma_offset_node, vm_rb);
+
+		if (node->vm_node.start < iter_node->vm_node.start)
+			iter = &(*iter)->rb_left;
+		else if (node->vm_node.start > iter_node->vm_node.start)
+			iter = &(*iter)->rb_right;
+		else
+			BUG();
+	}
+
+	rb_link_node(&node->vm_rb, parent, iter);
+	rb_insert_color(&node->vm_rb, &mgr->vm_addr_space_rb);
+}
+
+/**
+ * drm_vma_offset_add() - Add offset node to manager
+ * @mgr: Manager object
+ * @node: Node to be added
+ * @pages: Allocation size visible to user-space (in number of pages)
+ *
+ * Add a node to the offset-manager. If the node was already added, this does
+ * nothing and return 0. @pages is the size of the object given in number of
+ * pages.
+ * After this call succeeds, you can access the offset of the node until it
+ * is removed again.
+ *
+ * If this call fails, it is safe to retry the operation or call
+ * drm_vma_offset_remove(), anyway. However, no cleanup is required in that
+ * case.
+ *
+ * @pages is not required to be the same size as the underlying memory object
+ * that you want to map. It only limits the size that user-space can map into
+ * their address space.
+ *
+ * RETURNS:
+ * 0 on success, negative error code on failure.
+ */
+int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
+		       struct drm_vma_offset_node *node, unsigned long pages)
+{
+	int ret;
+
+	write_lock(&mgr->vm_lock);
+
+	if (drm_mm_node_allocated(&node->vm_node)) {
+		ret = 0;
+		goto out_unlock;
+	}
+
+	ret = drm_mm_insert_node_generic(&mgr->vm_addr_space_mm,
+					 &node->vm_node, pages, 0, 0);
+	if (ret)
+		goto out_unlock;
+
+	_drm_vma_offset_add_rb(mgr, node);
+
+out_unlock:
+	write_unlock(&mgr->vm_lock);
+	return ret;
+}
+EXPORT_SYMBOL(drm_vma_offset_add);
+
+/**
+ * drm_vma_offset_remove() - Remove offset node from manager
+ * @mgr: Manager object
+ * @node: Node to be removed
+ *
+ * Remove a node from the offset manager. If the node wasn't added before, this
+ * does nothing. After this call returns, the offset and size will be 0 until a
+ * new offset is allocated via drm_vma_offset_add() again. Helper functions like
+ * drm_vma_node_start() and drm_vma_node_offset_addr() will return 0 if no
+ * offset is allocated.
+ */
+void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
+			   struct drm_vma_offset_node *node)
+{
+	write_lock(&mgr->vm_lock);
+
+	if (drm_mm_node_allocated(&node->vm_node)) {
+		rb_erase(&node->vm_rb, &mgr->vm_addr_space_rb);
+		drm_mm_remove_node(&node->vm_node);
+		memset(&node->vm_node, 0, sizeof(node->vm_node));
+	}
+
+	write_unlock(&mgr->vm_lock);
+}
+EXPORT_SYMBOL(drm_vma_offset_remove);
diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h
new file mode 100644
index 0000000..7ee8c4b
--- /dev/null
+++ b/include/drm/drm_vma_manager.h
@@ -0,0 +1,202 @@
+#ifndef __DRM_VMA_MANAGER_H__
+#define __DRM_VMA_MANAGER_H__
+
+/*
+ * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drm_mm.h>
+#include <linux/module.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+struct drm_vma_offset_node {
+	struct drm_mm_node vm_node;
+	struct rb_node vm_rb;
+};
+
+struct drm_vma_offset_manager {
+	rwlock_t vm_lock;
+	struct rb_root vm_addr_space_rb;
+	struct drm_mm vm_addr_space_mm;
+};
+
+void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
+				 unsigned long page_offset, unsigned long size);
+void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr);
+
+struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr,
+						  unsigned long start,
+						  unsigned long pages);
+struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
+							   unsigned long start,
+							   unsigned long pages);
+int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
+		       struct drm_vma_offset_node *node, unsigned long pages);
+void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
+			   struct drm_vma_offset_node *node);
+
+/**
+ * drm_vma_offset_exact_lookup() - Look up node by exact address
+ * @mgr: Manager object
+ * @start: Start address (page-based, not byte-based)
+ * @pages: Size of object (page-based)
+ *
+ * Same as drm_vma_offset_lookup() but does not allow any offset into the node.
+ * It only returns the exact object with the given start address.
+ *
+ * RETURNS:
+ * Node at exact start address @start.
+ */
+static inline struct drm_vma_offset_node *
+drm_vma_offset_exact_lookup(struct drm_vma_offset_manager *mgr,
+			    unsigned long start,
+			    unsigned long pages)
+{
+	struct drm_vma_offset_node *node;
+
+	node = drm_vma_offset_lookup(mgr, start, pages);
+	return (node && node->vm_node.start == start) ? node : NULL;
+}
+
+/**
+ * drm_vma_offset_lock_lookup() - Lock lookup for extended private use
+ * @mgr: Manager object
+ *
+ * Lock VMA manager for extended lookups. Only *_locked() VMA function calls
+ * are allowed while holding this lock. All other contexts are blocked from VMA
+ * until the lock is released via drm_vma_offset_unlock_lookup().
+ *
+ * Use this if you need to take a reference to the objects returned by
+ * drm_vma_offset_lookup_locked() before releasing this lock again.
+ *
+ * This lock must not be used for anything else than extended lookups. You must
+ * not call any other VMA helpers while holding this lock.
+ *
+ * Note: You're in atomic-context while holding this lock!
+ *
+ * Example:
+ *   drm_vma_offset_lock_lookup(mgr);
+ *   node = drm_vma_offset_lookup_locked(mgr);
+ *   if (node)
+ *       kref_get_unless_zero(container_of(node, sth, entr));
+ *   drm_vma_offset_unlock_lookup(mgr);
+ */
+static inline void drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr)
+{
+	read_lock(&mgr->vm_lock);
+}
+
+/**
+ * drm_vma_offset_unlock_lookup() - Unlock lookup for extended private use
+ * @mgr: Manager object
+ *
+ * Release lookup-lock. See drm_vma_offset_lock_lookup() for more information.
+ */
+static inline void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr)
+{
+	read_unlock(&mgr->vm_lock);
+}
+
+/**
+ * drm_vma_node_reset() - Initialize or reset node object
+ * @node: Node to initialize or reset
+ *
+ * Reset a node to its initial state. This must be called if @node isn't
+ * already cleared (eg., via kzalloc) before using it with any VMA offset
+ * manager.
+ *
+ * This must not be called on an already allocated node, or you will leak
+ * memory.
+ */
+static inline void drm_vma_node_reset(struct drm_vma_offset_node *node)
+{
+	memset(node, 0, sizeof(*node));
+}
+
+/**
+ * drm_vma_node_start() - Return start address for page-based addressing
+ * @node: Node to inspect
+ *
+ * Return the start address of the given node. This can be used as offset into
+ * the linear VM space that is provided by the VMA offset manager. Note that
+ * this can only be used for page-based addressing. If you need a proper offset
+ * for user-space mappings, you must apply "<< PAGE_SHIFT" or use the
+ * drm_vma_node_offset_addr() helper instead.
+ *
+ * RETURNS:
+ * Start address of @node for page-based addressing. 0 if the node does not
+ * have an offset allocated.
+ */
+static inline unsigned long drm_vma_node_start(struct drm_vma_offset_node *node)
+{
+	return node->vm_node.start;
+}
+
+/**
+ * drm_vma_node_size() - Return size (page-based)
+ * @node: Node to inspect
+ *
+ * Return the size as number of pages for the given node. This is the same size
+ * that was passed to drm_vma_offset_add(). If no offset is allocated for the
+ * node, this is 0.
+ *
+ * RETURNS:
+ * Size of @node as number of pages. 0 if the node does not have an offset
+ * allocated.
+ */
+static inline unsigned long drm_vma_node_size(struct drm_vma_offset_node *node)
+{
+	return node->vm_node.size;
+}
+
+/**
+ * drm_vma_node_has_offset() - Check whether node is added to offset manager
+ * @node: Node to be checked
+ *
+ * RETURNS:
+ * true iff the node was previously allocated an offset and added to
+ * an vma offset manager.
+ */
+static inline bool drm_vma_node_has_offset(struct drm_vma_offset_node *node)
+{
+	return drm_mm_node_allocated(&node->vm_node);
+}
+
+/**
+ * drm_vma_node_offset_addr() - Return sanitized offset for user-space mmaps
+ * @node: Linked offset node
+ *
+ * Same as drm_vma_node_start() but returns the address as a valid offset that
+ * can be used for user-space mappings during mmap().
+ * This must not be called on unlinked nodes.
+ *
+ * RETURNS:
+ * Offset of @node for byte-based addressing. 0 if the node does not have an
+ * object allocated.
+ */
+static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
+{
+	return ((__u64)node->vm_node.start) << PAGE_SHIFT;
+}
+
+#endif /* __DRM_VMA_MANAGER_H__ */
-- 
1.8.3.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 2/4] drm/gem: convert to new unified vma manager
  2013-07-23 12:47   ` [PATCH v4 2/4] drm/gem: convert to new unified vma manager David Herrmann
  2013-07-24 15:52     ` Daniel Vetter
@ 2013-07-24 19:07     ` David Herrmann
  1 sibling, 0 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-24 19:07 UTC (permalink / raw)
  To: dri-devel; +Cc: Dave Airlie

Use the new vma manager instead of the old hashtable. Also convert all
drivers to use the new convenience helpers. This drops all the
(map_list.hash.key << PAGE_SHIFT) non-sense.

Locking and access-management is exactly the same as before with an
additional lock inside of the vma-manager, which strictly wouldn't be
needed for gem.

v2:
 - rebase on drm-next
 - init nodes via drm_vma_node_reset() in drm_gem.c
v3:
 - fix tegra
v4:
 - remove duplicate if (drm_vma_node_has_offset()) checks
 - inline now trivial drm_vma_node_offset_addr() calls
v5:
 - skip node-reset on gem-init due to kzalloc()
 - do not allow mapping gem-objects with offsets (backwards compat)
 - remove unneccessary casts

Cc: Inki Dae <inki.dae@samsung.com>
Cc: Rob Clark <robdclark@gmail.com>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---
 drivers/gpu/drm/drm_gem.c                  | 89 +++++-------------------------
 drivers/gpu/drm/drm_gem_cma_helper.c       | 16 ++----
 drivers/gpu/drm/exynos/exynos_drm_gem.c    | 14 ++---
 drivers/gpu/drm/gma500/gem.c               | 15 ++---
 drivers/gpu/drm/i915/i915_gem.c            | 10 ++--
 drivers/gpu/drm/omapdrm/omap_gem.c         | 28 +++++-----
 drivers/gpu/drm/omapdrm/omap_gem_helpers.c | 49 +---------------
 drivers/gpu/drm/udl/udl_gem.c              | 13 ++---
 drivers/gpu/host1x/drm/gem.c               |  5 +-
 include/drm/drmP.h                         |  7 +--
 include/uapi/drm/drm.h                     |  2 +-
 11 files changed, 62 insertions(+), 186 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 1ad9e7e..3613b50 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -37,6 +37,7 @@
 #include <linux/shmem_fs.h>
 #include <linux/dma-buf.h>
 #include <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
 
 /** @file drm_gem.c
  *
@@ -102,14 +103,9 @@ drm_gem_init(struct drm_device *dev)
 	}
 
 	dev->mm_private = mm;
-
-	if (drm_ht_create(&mm->offset_hash, 12)) {
-		kfree(mm);
-		return -ENOMEM;
-	}
-
-	drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START,
-		    DRM_FILE_PAGE_OFFSET_SIZE);
+	drm_vma_offset_manager_init(&mm->vma_manager,
+				    DRM_FILE_PAGE_OFFSET_START,
+				    DRM_FILE_PAGE_OFFSET_SIZE);
 
 	return 0;
 }
@@ -119,8 +115,7 @@ drm_gem_destroy(struct drm_device *dev)
 {
 	struct drm_gem_mm *mm = dev->mm_private;
 
-	drm_mm_takedown(&mm->offset_manager);
-	drm_ht_remove(&mm->offset_hash);
+	drm_vma_offset_manager_destroy(&mm->vma_manager);
 	kfree(mm);
 	dev->mm_private = NULL;
 }
@@ -302,12 +297,8 @@ drm_gem_free_mmap_offset(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
 	struct drm_gem_mm *mm = dev->mm_private;
-	struct drm_map_list *list = &obj->map_list;
 
-	drm_ht_remove_item(&mm->offset_hash, &list->hash);
-	drm_mm_put_block(list->file_offset_node);
-	kfree(list->map);
-	list->map = NULL;
+	drm_vma_offset_remove(&mm->vma_manager, &obj->vma_node);
 }
 EXPORT_SYMBOL(drm_gem_free_mmap_offset);
 
@@ -327,54 +318,9 @@ drm_gem_create_mmap_offset(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
 	struct drm_gem_mm *mm = dev->mm_private;
-	struct drm_map_list *list;
-	struct drm_local_map *map;
-	int ret;
-
-	/* Set the object up for mmap'ing */
-	list = &obj->map_list;
-	list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
-	if (!list->map)
-		return -ENOMEM;
-
-	map = list->map;
-	map->type = _DRM_GEM;
-	map->size = obj->size;
-	map->handle = obj;
-
-	/* Get a DRM GEM mmap offset allocated... */
-	list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
-			obj->size / PAGE_SIZE, 0, false);
-
-	if (!list->file_offset_node) {
-		DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
-		ret = -ENOSPC;
-		goto out_free_list;
-	}
 
-	list->file_offset_node = drm_mm_get_block(list->file_offset_node,
-			obj->size / PAGE_SIZE, 0);
-	if (!list->file_offset_node) {
-		ret = -ENOMEM;
-		goto out_free_list;
-	}
-
-	list->hash.key = list->file_offset_node->start;
-	ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
-	if (ret) {
-		DRM_ERROR("failed to add to map hash\n");
-		goto out_free_mm;
-	}
-
-	return 0;
-
-out_free_mm:
-	drm_mm_put_block(list->file_offset_node);
-out_free_list:
-	kfree(list->map);
-	list->map = NULL;
-
-	return ret;
+	return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node,
+				  obj->size / PAGE_SIZE);
 }
 EXPORT_SYMBOL(drm_gem_create_mmap_offset);
 
@@ -703,8 +649,8 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
 	struct drm_file *priv = filp->private_data;
 	struct drm_device *dev = priv->minor->dev;
 	struct drm_gem_mm *mm = dev->mm_private;
-	struct drm_local_map *map = NULL;
-	struct drm_hash_item *hash;
+	struct drm_gem_object *obj;
+	struct drm_vma_offset_node *node;
 	int ret = 0;
 
 	if (drm_device_is_unplugged(dev))
@@ -712,21 +658,16 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
 
 	mutex_lock(&dev->struct_mutex);
 
-	if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
+	node = drm_vma_offset_exact_lookup(&mm->vma_manager, vma->vm_pgoff,
+					   vma_pages(vma));
+	if (!node) {
 		mutex_unlock(&dev->struct_mutex);
 		return drm_mmap(filp, vma);
 	}
 
-	map = drm_hash_entry(hash, struct drm_map_list, hash)->map;
-	if (!map ||
-	    ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) {
-		ret =  -EPERM;
-		goto out_unlock;
-	}
-
-	ret = drm_gem_mmap_obj(map->handle, map->size, vma);
+	obj = container_of(node, struct drm_gem_object, vma_node);
+	ret = drm_gem_mmap_obj(obj, drm_vma_node_size(node), vma);
 
-out_unlock:
 	mutex_unlock(&dev->struct_mutex);
 
 	return ret;
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
index ece72a8..847f091 100644
--- a/drivers/gpu/drm/drm_gem_cma_helper.c
+++ b/drivers/gpu/drm/drm_gem_cma_helper.c
@@ -27,11 +27,7 @@
 #include <drm/drmP.h>
 #include <drm/drm.h>
 #include <drm/drm_gem_cma_helper.h>
-
-static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
-{
-	return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
-}
+#include <drm/drm_vma_manager.h>
 
 /*
  * __drm_gem_cma_create - Create a GEM CMA object without allocating memory
@@ -172,8 +168,7 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
 {
 	struct drm_gem_cma_object *cma_obj;
 
-	if (gem_obj->map_list.map)
-		drm_gem_free_mmap_offset(gem_obj);
+	drm_gem_free_mmap_offset(gem_obj);
 
 	cma_obj = to_drm_gem_cma_obj(gem_obj);
 
@@ -237,7 +232,7 @@ int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
 		return -EINVAL;
 	}
 
-	*offset = get_gem_mmap_offset(gem_obj);
+	*offset = drm_vma_node_offset_addr(&gem_obj->vma_node);
 
 	drm_gem_object_unreference(gem_obj);
 
@@ -301,12 +296,11 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m
 {
 	struct drm_gem_object *obj = &cma_obj->base;
 	struct drm_device *dev = obj->dev;
-	uint64_t off = 0;
+	uint64_t off;
 
 	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
-	if (obj->map_list.map)
-		off = (uint64_t)obj->map_list.hash.key;
+	off = drm_vma_node_start(&obj->vma_node);
 
 	seq_printf(m, "%2d (%2d) %08llx %08Zx %p %d",
 			obj->name, obj->refcount.refcount.counter,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index 24c22a8..be32db1 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -10,6 +10,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
 
 #include <linux/shmem_fs.h>
 #include <drm/exynos_drm.h>
@@ -152,8 +153,7 @@ out:
 	exynos_drm_fini_buf(obj->dev, buf);
 	exynos_gem_obj->buffer = NULL;
 
-	if (obj->map_list.map)
-		drm_gem_free_mmap_offset(obj);
+	drm_gem_free_mmap_offset(obj);
 
 	/* release file pointer to gem object. */
 	drm_gem_object_release(obj);
@@ -703,13 +703,11 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
 		goto unlock;
 	}
 
-	if (!obj->map_list.map) {
-		ret = drm_gem_create_mmap_offset(obj);
-		if (ret)
-			goto out;
-	}
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret)
+		goto out;
 
-	*offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
+	*offset = drm_vma_node_offset_addr(&obj->vma_node);
 	DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
 
 out:
diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c
index fe1d332..2f77bea 100644
--- a/drivers/gpu/drm/gma500/gem.c
+++ b/drivers/gpu/drm/gma500/gem.c
@@ -26,6 +26,7 @@
 #include <drm/drmP.h>
 #include <drm/drm.h>
 #include <drm/gma_drm.h>
+#include <drm/drm_vma_manager.h>
 #include "psb_drv.h"
 
 int psb_gem_init_object(struct drm_gem_object *obj)
@@ -38,8 +39,7 @@ void psb_gem_free_object(struct drm_gem_object *obj)
 	struct gtt_range *gtt = container_of(obj, struct gtt_range, gem);
 
 	/* Remove the list map if one is present */
-	if (obj->map_list.map)
-		drm_gem_free_mmap_offset(obj);
+	drm_gem_free_mmap_offset(obj);
 	drm_gem_object_release(obj);
 
 	/* This must occur last as it frees up the memory of the GEM object */
@@ -81,13 +81,10 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev,
 	/* What validation is needed here ? */
 
 	/* Make it mmapable */
-	if (!obj->map_list.map) {
-		ret = drm_gem_create_mmap_offset(obj);
-		if (ret)
-			goto out;
-	}
-	/* GEM should really work out the hash offsets for us */
-	*offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret)
+		goto out;
+	*offset = drm_vma_node_offset_addr(&obj->vma_node);
 out:
 	drm_gem_object_unreference(obj);
 unlock:
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 46bf7e3..53f81b3 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -26,6 +26,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 #include "i915_trace.h"
@@ -1428,7 +1429,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
 
 	if (obj->base.dev->dev_mapping)
 		unmap_mapping_range(obj->base.dev->dev_mapping,
-				    (loff_t)obj->base.map_list.hash.key<<PAGE_SHIFT,
+				    (loff_t)drm_vma_node_offset_addr(&obj->base.vma_node),
 				    obj->base.size, 1);
 
 	obj->fault_mappable = false;
@@ -1486,7 +1487,7 @@ static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj)
 	struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
 	int ret;
 
-	if (obj->base.map_list.map)
+	if (drm_vma_node_has_offset(&obj->base.vma_node))
 		return 0;
 
 	dev_priv->mm.shrinker_no_lock_stealing = true;
@@ -1517,9 +1518,6 @@ out:
 
 static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj)
 {
-	if (!obj->base.map_list.map)
-		return;
-
 	drm_gem_free_mmap_offset(&obj->base);
 }
 
@@ -1558,7 +1556,7 @@ i915_gem_mmap_gtt(struct drm_file *file,
 	if (ret)
 		goto out;
 
-	*offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT;
+	*offset = drm_vma_node_offset_addr(&obj->base.vma_node);
 
 out:
 	drm_gem_object_unreference(&obj->base);
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
index cbcd71e..f90531fc00 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -20,6 +20,7 @@
 
 #include <linux/spinlock.h>
 #include <linux/shmem_fs.h>
+#include <drm/drm_vma_manager.h>
 
 #include "omap_drv.h"
 #include "omap_dmm_tiler.h"
@@ -308,21 +309,20 @@ uint32_t omap_gem_flags(struct drm_gem_object *obj)
 static uint64_t mmap_offset(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
+	int ret;
+	size_t size;
 
 	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
-	if (!obj->map_list.map) {
-		/* Make it mmapable */
-		size_t size = omap_gem_mmap_size(obj);
-		int ret = _drm_gem_create_mmap_offset_size(obj, size);
-
-		if (ret) {
-			dev_err(dev->dev, "could not allocate mmap offset\n");
-			return 0;
-		}
+	/* Make it mmapable */
+	size = omap_gem_mmap_size(obj);
+	ret = _drm_gem_create_mmap_offset_size(obj, size);
+	if (ret) {
+		dev_err(dev->dev, "could not allocate mmap offset\n");
+		return 0;
 	}
 
-	return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT;
+	return drm_vma_node_offset_addr(&obj->vma_node);
 }
 
 uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
@@ -997,12 +997,11 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
 {
 	struct drm_device *dev = obj->dev;
 	struct omap_gem_object *omap_obj = to_omap_bo(obj);
-	uint64_t off = 0;
+	uint64_t off;
 
 	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
-	if (obj->map_list.map)
-		off = (uint64_t)obj->map_list.hash.key;
+	off = drm_vma_node_start(&obj->vma_node);
 
 	seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d",
 			omap_obj->flags, obj->name, obj->refcount.refcount.counter,
@@ -1309,8 +1308,7 @@ void omap_gem_free_object(struct drm_gem_object *obj)
 
 	list_del(&omap_obj->mm_list);
 
-	if (obj->map_list.map)
-		drm_gem_free_mmap_offset(obj);
+	drm_gem_free_mmap_offset(obj);
 
 	/* this means the object is still pinned.. which really should
 	 * not happen.  I think..
diff --git a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
index f9eb679..dbb1575 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c
@@ -118,52 +118,7 @@ _drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size)
 {
 	struct drm_device *dev = obj->dev;
 	struct drm_gem_mm *mm = dev->mm_private;
-	struct drm_map_list *list;
-	struct drm_local_map *map;
-	int ret = 0;
-
-	/* Set the object up for mmap'ing */
-	list = &obj->map_list;
-	list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
-	if (!list->map)
-		return -ENOMEM;
-
-	map = list->map;
-	map->type = _DRM_GEM;
-	map->size = size;
-	map->handle = obj;
-
-	/* Get a DRM GEM mmap offset allocated... */
-	list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
-			size / PAGE_SIZE, 0, 0);
-
-	if (!list->file_offset_node) {
-		DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
-		ret = -ENOSPC;
-		goto out_free_list;
-	}
-
-	list->file_offset_node = drm_mm_get_block(list->file_offset_node,
-			size / PAGE_SIZE, 0);
-	if (!list->file_offset_node) {
-		ret = -ENOMEM;
-		goto out_free_list;
-	}
-
-	list->hash.key = list->file_offset_node->start;
-	ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
-	if (ret) {
-		DRM_ERROR("failed to add to map hash\n");
-		goto out_free_mm;
-	}
-
-	return 0;
-
-out_free_mm:
-	drm_mm_put_block(list->file_offset_node);
-out_free_list:
-	kfree(list->map);
-	list->map = NULL;
 
-	return ret;
+	return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node,
+				  size / PAGE_SIZE);
 }
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
index ef034fa..2a4cb2f 100644
--- a/drivers/gpu/drm/udl/udl_gem.c
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -223,8 +223,7 @@ void udl_gem_free_object(struct drm_gem_object *gem_obj)
 	if (obj->pages)
 		udl_gem_put_pages(obj);
 
-	if (gem_obj->map_list.map)
-		drm_gem_free_mmap_offset(gem_obj);
+	drm_gem_free_mmap_offset(gem_obj);
 }
 
 /* the dumb interface doesn't work with the GEM straight MMAP
@@ -247,13 +246,11 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev,
 	ret = udl_gem_get_pages(gobj, GFP_KERNEL);
 	if (ret)
 		goto out;
-	if (!gobj->base.map_list.map) {
-		ret = drm_gem_create_mmap_offset(obj);
-		if (ret)
-			goto out;
-	}
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret)
+		goto out;
 
-	*offset = (u64)gobj->base.map_list.hash.key << PAGE_SHIFT;
+	*offset = drm_vma_node_offset_addr(&gobj->base.vma_node);
 
 out:
 	drm_gem_object_unreference(&gobj->base);
diff --git a/drivers/gpu/host1x/drm/gem.c b/drivers/gpu/host1x/drm/gem.c
index c5e9a9b..bc323b3 100644
--- a/drivers/gpu/host1x/drm/gem.c
+++ b/drivers/gpu/host1x/drm/gem.c
@@ -108,7 +108,7 @@ static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo)
 
 unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo)
 {
-	return (unsigned int)bo->gem.map_list.hash.key << PAGE_SHIFT;
+	return (unsigned int)drm_vma_node_offset_addr(&bo->gem.vma_node);
 }
 
 struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
@@ -182,8 +182,7 @@ void tegra_bo_free_object(struct drm_gem_object *gem)
 {
 	struct tegra_bo *bo = to_tegra_bo(gem);
 
-	if (gem->map_list.map)
-		drm_gem_free_mmap_offset(gem);
+	drm_gem_free_mmap_offset(gem);
 
 	drm_gem_object_release(gem);
 	tegra_bo_destroy(gem->dev, bo);
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 0ab6a09..4b518e0 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -71,6 +71,7 @@
 #include <asm/pgalloc.h>
 #include <drm/drm.h>
 #include <drm/drm_sarea.h>
+#include <drm/drm_vma_manager.h>
 
 #include <linux/idr.h>
 
@@ -587,7 +588,6 @@ struct drm_map_list {
 	struct drm_local_map *map;	/**< mapping */
 	uint64_t user_token;
 	struct drm_master *master;
-	struct drm_mm_node *file_offset_node;	/**< fake offset */
 };
 
 /**
@@ -622,8 +622,7 @@ struct drm_ati_pcigart_info {
  * GEM specific mm private for tracking GEM objects
  */
 struct drm_gem_mm {
-	struct drm_mm offset_manager;	/**< Offset mgmt for buffer objects */
-	struct drm_open_hash offset_hash; /**< User token hash table for maps */
+	struct drm_vma_offset_manager vma_manager;
 };
 
 /**
@@ -644,7 +643,7 @@ struct drm_gem_object {
 	struct file *filp;
 
 	/* Mapping info for this object */
-	struct drm_map_list map_list;
+	struct drm_vma_offset_node vma_node;
 
 	/**
 	 * Size of the object, in bytes.  Immutable over the object's
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index 238a166..272580c 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -181,7 +181,7 @@ enum drm_map_type {
 	_DRM_AGP = 3,		  /**< AGP/GART */
 	_DRM_SCATTER_GATHER = 4,  /**< Scatter/gather memory for PCI DMA */
 	_DRM_CONSISTENT = 5,	  /**< Consistent memory for PCI DMA */
-	_DRM_GEM = 6,		  /**< GEM object */
+	_DRM_GEM = 6,		  /**< GEM object (obsolete) */
 };
 
 /**
-- 
1.8.3.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 3/4] drm/ttm: convert to unified vma offset manager
  2013-07-23 12:47   ` [PATCH v4 3/4] drm/ttm: convert to unified vma offset manager David Herrmann
  2013-07-24 15:57     ` Daniel Vetter
@ 2013-07-24 19:08     ` David Herrmann
  1 sibling, 0 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-24 19:08 UTC (permalink / raw)
  To: dri-devel
  Cc: Thomas Hellstrom, Martin Peres, Alex Deucher, Ben Skeggs, Dave Airlie

Use the new vma-manager infrastructure. This doesn't change any
implementation details as the vma-offset-manager is nearly copied 1-to-1
from TTM.

The vm_lock is moved into the offset manager so we can drop it from TTM.
During lookup, we use the vma locking helpers to take a reference to the
found object.
In all other scenarios, locking stays the same as before. We always
guarantee that drm_vma_offset_remove() is called only during destruction.
Hence, helpers like drm_vma_node_offset_addr() are always safe as long as
the node has a valid offset.

This also drops the addr_space_offset member as it is a copy of vm_start
in vma_node objects. Use the accessor functions instead.

v4:
 - remove vm_lock
 - use drm_vma_offset_lock_lookup() to protect lookup (instead of vm_lock)

Cc: Dave Airlie <airlied@redhat.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Cc: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Cc: Martin Peres <martin.peres@labri.fr>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---
 drivers/gpu/drm/ast/ast_main.c            |  2 +-
 drivers/gpu/drm/cirrus/cirrus_main.c      |  2 +-
 drivers/gpu/drm/mgag200/mgag200_main.c    |  2 +-
 drivers/gpu/drm/nouveau/nouveau_display.c |  2 +-
 drivers/gpu/drm/nouveau/nouveau_gem.c     |  2 +-
 drivers/gpu/drm/qxl/qxl_object.h          |  2 +-
 drivers/gpu/drm/qxl/qxl_release.c         |  2 +-
 drivers/gpu/drm/radeon/radeon_object.h    |  5 +-
 drivers/gpu/drm/ttm/ttm_bo.c              | 89 ++++++-------------------------
 drivers/gpu/drm/ttm/ttm_bo_util.c         |  3 +-
 drivers/gpu/drm/ttm/ttm_bo_vm.c           | 81 +++++++++++-----------------
 drivers/gpu/drm/vmwgfx/vmwgfx_resource.c  |  4 +-
 include/drm/ttm/ttm_bo_api.h              | 15 ++----
 include/drm/ttm/ttm_bo_driver.h           | 10 ++--
 14 files changed, 66 insertions(+), 155 deletions(-)

diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
index f60fd7b..c195dc2 100644
--- a/drivers/gpu/drm/ast/ast_main.c
+++ b/drivers/gpu/drm/ast/ast_main.c
@@ -487,7 +487,7 @@ void ast_gem_free_object(struct drm_gem_object *obj)
 
 static inline u64 ast_bo_mmap_offset(struct ast_bo *bo)
 {
-	return bo->bo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->bo.vma_node);
 }
 int
 ast_dumb_mmap_offset(struct drm_file *file,
diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c
index 35cbae8..3a7a0ef 100644
--- a/drivers/gpu/drm/cirrus/cirrus_main.c
+++ b/drivers/gpu/drm/cirrus/cirrus_main.c
@@ -294,7 +294,7 @@ void cirrus_gem_free_object(struct drm_gem_object *obj)
 
 static inline u64 cirrus_bo_mmap_offset(struct cirrus_bo *bo)
 {
-	return bo->bo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->bo.vma_node);
 }
 
 int
diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c
index 9fa5685..1a75ea3 100644
--- a/drivers/gpu/drm/mgag200/mgag200_main.c
+++ b/drivers/gpu/drm/mgag200/mgag200_main.c
@@ -349,7 +349,7 @@ void mgag200_gem_free_object(struct drm_gem_object *obj)
 
 static inline u64 mgag200_bo_mmap_offset(struct mgag200_bo *bo)
 {
-	return bo->bo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->bo.vma_node);
 }
 
 int
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 708b2d1..7a8caa1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -697,7 +697,7 @@ nouveau_display_dumb_map_offset(struct drm_file *file_priv,
 	gem = drm_gem_object_lookup(dev, file_priv, handle);
 	if (gem) {
 		struct nouveau_bo *bo = gem->driver_private;
-		*poffset = bo->bo.addr_space_offset;
+		*poffset = drm_vma_node_offset_addr(&bo->bo.vma_node);
 		drm_gem_object_unreference_unlocked(gem);
 		return 0;
 	}
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index e72d09c..86597eb 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -226,7 +226,7 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem,
 	}
 
 	rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
-	rep->map_handle = nvbo->bo.addr_space_offset;
+	rep->map_handle = drm_vma_node_offset_addr(&nvbo->bo.vma_node);
 	rep->tile_mode = nvbo->tile_mode;
 	rep->tile_flags = nvbo->tile_flags;
 	return 0;
diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h
index ee7ad79..af10165 100644
--- a/drivers/gpu/drm/qxl/qxl_object.h
+++ b/drivers/gpu/drm/qxl/qxl_object.h
@@ -59,7 +59,7 @@ static inline unsigned long qxl_bo_size(struct qxl_bo *bo)
 
 static inline u64 qxl_bo_mmap_offset(struct qxl_bo *bo)
 {
-	return bo->tbo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->tbo.vma_node);
 }
 
 static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type,
diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c
index b443d67..1a648e1 100644
--- a/drivers/gpu/drm/qxl/qxl_release.c
+++ b/drivers/gpu/drm/qxl/qxl_release.c
@@ -87,7 +87,7 @@ qxl_release_free(struct qxl_device *qdev,
 
 	for (i = 0 ; i < release->bo_count; ++i) {
 		QXL_INFO(qdev, "release %llx\n",
-			release->bos[i]->tbo.addr_space_offset
+			drm_vma_node_offset_addr(&release->bos[i]->tbo.vma_node)
 						- DRM_FILE_OFFSET);
 		qxl_fence_remove_release(&release->bos[i]->fence, release->id);
 		qxl_bo_unref(&release->bos[i]);
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index 49c82c4..209b111 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -113,13 +113,10 @@ static inline unsigned radeon_bo_gpu_page_alignment(struct radeon_bo *bo)
  * @bo:	radeon object for which we query the offset
  *
  * Returns mmap offset of the object.
- *
- * Note: addr_space_offset is constant after ttm bo init thus isn't protected
- * by any lock.
  */
 static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo)
 {
-	return bo->tbo.addr_space_offset;
+	return drm_vma_node_offset_addr(&bo->tbo.vma_node);
 }
 
 extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index cb9dd67..3dc08b6 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -615,13 +615,7 @@ static void ttm_bo_release(struct kref *kref)
 	struct ttm_bo_device *bdev = bo->bdev;
 	struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type];
 
-	write_lock(&bdev->vm_lock);
-	if (likely(bo->vm_node != NULL)) {
-		rb_erase(&bo->vm_rb, &bdev->addr_space_rb);
-		drm_mm_put_block(bo->vm_node);
-		bo->vm_node = NULL;
-	}
-	write_unlock(&bdev->vm_lock);
+	drm_vma_offset_remove(&bdev->vma_manager, &bo->vma_node);
 	ttm_mem_io_lock(man, false);
 	ttm_mem_io_free_vm(bo);
 	ttm_mem_io_unlock(man);
@@ -1129,6 +1123,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
 	bo->resv = &bo->ttm_resv;
 	reservation_object_init(bo->resv);
 	atomic_inc(&bo->glob->bo_count);
+	drm_vma_node_reset(&bo->vma_node);
 
 	ret = ttm_bo_check_placement(bo, placement);
 
@@ -1424,10 +1419,7 @@ int ttm_bo_device_release(struct ttm_bo_device *bdev)
 		TTM_DEBUG("Swap list was clean\n");
 	spin_unlock(&glob->lru_lock);
 
-	BUG_ON(!drm_mm_clean(&bdev->addr_space_mm));
-	write_lock(&bdev->vm_lock);
-	drm_mm_takedown(&bdev->addr_space_mm);
-	write_unlock(&bdev->vm_lock);
+	drm_vma_offset_manager_destroy(&bdev->vma_manager);
 
 	return ret;
 }
@@ -1441,7 +1433,6 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
 {
 	int ret = -EINVAL;
 
-	rwlock_init(&bdev->vm_lock);
 	bdev->driver = driver;
 
 	memset(bdev->man, 0, sizeof(bdev->man));
@@ -1454,9 +1445,8 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
 	if (unlikely(ret != 0))
 		goto out_no_sys;
 
-	bdev->addr_space_rb = RB_ROOT;
-	drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000);
-
+	drm_vma_offset_manager_init(&bdev->vma_manager, file_page_offset,
+				    0x10000000);
 	INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue);
 	INIT_LIST_HEAD(&bdev->ddestroy);
 	bdev->dev_mapping = NULL;
@@ -1498,12 +1488,17 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
 void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo)
 {
 	struct ttm_bo_device *bdev = bo->bdev;
-	loff_t offset = (loff_t) bo->addr_space_offset;
-	loff_t holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
+	loff_t offset, holelen;
 
 	if (!bdev->dev_mapping)
 		return;
-	unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
+
+	if (drm_vma_node_has_offset(&bo->vma_node)) {
+		offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node);
+		holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
+
+		unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
+	}
 	ttm_mem_io_free_vm(bo);
 }
 
@@ -1520,31 +1515,6 @@ void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo)
 
 EXPORT_SYMBOL(ttm_bo_unmap_virtual);
 
-static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo)
-{
-	struct ttm_bo_device *bdev = bo->bdev;
-	struct rb_node **cur = &bdev->addr_space_rb.rb_node;
-	struct rb_node *parent = NULL;
-	struct ttm_buffer_object *cur_bo;
-	unsigned long offset = bo->vm_node->start;
-	unsigned long cur_offset;
-
-	while (*cur) {
-		parent = *cur;
-		cur_bo = rb_entry(parent, struct ttm_buffer_object, vm_rb);
-		cur_offset = cur_bo->vm_node->start;
-		if (offset < cur_offset)
-			cur = &parent->rb_left;
-		else if (offset > cur_offset)
-			cur = &parent->rb_right;
-		else
-			BUG();
-	}
-
-	rb_link_node(&bo->vm_rb, parent, cur);
-	rb_insert_color(&bo->vm_rb, &bdev->addr_space_rb);
-}
-
 /**
  * ttm_bo_setup_vm:
  *
@@ -1559,38 +1529,9 @@ static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo)
 static int ttm_bo_setup_vm(struct ttm_buffer_object *bo)
 {
 	struct ttm_bo_device *bdev = bo->bdev;
-	int ret;
-
-retry_pre_get:
-	ret = drm_mm_pre_get(&bdev->addr_space_mm);
-	if (unlikely(ret != 0))
-		return ret;
-
-	write_lock(&bdev->vm_lock);
-	bo->vm_node = drm_mm_search_free(&bdev->addr_space_mm,
-					 bo->mem.num_pages, 0, 0);
-
-	if (unlikely(bo->vm_node == NULL)) {
-		ret = -ENOMEM;
-		goto out_unlock;
-	}
 
-	bo->vm_node = drm_mm_get_block_atomic(bo->vm_node,
-					      bo->mem.num_pages, 0);
-
-	if (unlikely(bo->vm_node == NULL)) {
-		write_unlock(&bdev->vm_lock);
-		goto retry_pre_get;
-	}
-
-	ttm_bo_vm_insert_rb(bo);
-	write_unlock(&bdev->vm_lock);
-	bo->addr_space_offset = ((uint64_t) bo->vm_node->start) << PAGE_SHIFT;
-
-	return 0;
-out_unlock:
-	write_unlock(&bdev->vm_lock);
-	return ret;
+	return drm_vma_offset_add(&bdev->vma_manager, &bo->vma_node,
+				  bo->mem.num_pages);
 }
 
 int ttm_bo_wait(struct ttm_buffer_object *bo,
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 319cf41..7cc904d 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -30,6 +30,7 @@
 
 #include <drm/ttm/ttm_bo_driver.h>
 #include <drm/ttm/ttm_placement.h>
+#include <drm/drm_vma_manager.h>
 #include <linux/io.h>
 #include <linux/highmem.h>
 #include <linux/wait.h>
@@ -450,7 +451,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
 	INIT_LIST_HEAD(&fbo->lru);
 	INIT_LIST_HEAD(&fbo->swap);
 	INIT_LIST_HEAD(&fbo->io_reserve_lru);
-	fbo->vm_node = NULL;
+	drm_vma_node_reset(&fbo->vma_node);
 	atomic_set(&fbo->cpu_writers, 0);
 
 	spin_lock(&bdev->fence_lock);
diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
index 3df9f16..8c0e2c0 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
@@ -33,6 +33,7 @@
 #include <ttm/ttm_module.h>
 #include <ttm/ttm_bo_driver.h>
 #include <ttm/ttm_placement.h>
+#include <drm/drm_vma_manager.h>
 #include <linux/mm.h>
 #include <linux/rbtree.h>
 #include <linux/module.h>
@@ -40,37 +41,6 @@
 
 #define TTM_BO_VM_NUM_PREFAULT 16
 
-static struct ttm_buffer_object *ttm_bo_vm_lookup_rb(struct ttm_bo_device *bdev,
-						     unsigned long page_start,
-						     unsigned long num_pages)
-{
-	struct rb_node *cur = bdev->addr_space_rb.rb_node;
-	unsigned long cur_offset;
-	struct ttm_buffer_object *bo;
-	struct ttm_buffer_object *best_bo = NULL;
-
-	while (likely(cur != NULL)) {
-		bo = rb_entry(cur, struct ttm_buffer_object, vm_rb);
-		cur_offset = bo->vm_node->start;
-		if (page_start >= cur_offset) {
-			cur = cur->rb_right;
-			best_bo = bo;
-			if (page_start == cur_offset)
-				break;
-		} else
-			cur = cur->rb_left;
-	}
-
-	if (unlikely(best_bo == NULL))
-		return NULL;
-
-	if (unlikely((best_bo->vm_node->start + best_bo->num_pages) <
-		     (page_start + num_pages)))
-		return NULL;
-
-	return best_bo;
-}
-
 static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
 	struct ttm_buffer_object *bo = (struct ttm_buffer_object *)
@@ -146,9 +116,9 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 	}
 
 	page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) +
-	    bo->vm_node->start - vma->vm_pgoff;
+	    drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
 	page_last = vma_pages(vma) +
-	    bo->vm_node->start - vma->vm_pgoff;
+	    drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff;
 
 	if (unlikely(page_offset >= bo->num_pages)) {
 		retval = VM_FAULT_SIGBUS;
@@ -249,6 +219,30 @@ static const struct vm_operations_struct ttm_bo_vm_ops = {
 	.close = ttm_bo_vm_close
 };
 
+static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device *bdev,
+						  unsigned long offset,
+						  unsigned long pages)
+{
+	struct drm_vma_offset_node *node;
+	struct ttm_buffer_object *bo = NULL;
+
+	drm_vma_offset_lock_lookup(&bdev->vma_manager);
+
+	node = drm_vma_offset_lookup_locked(&bdev->vma_manager, offset, pages);
+	if (likely(node)) {
+		bo = container_of(node, struct ttm_buffer_object, vma_node);
+		if (!kref_get_unless_zero(&bo->kref))
+			bo = NULL;
+	}
+
+	drm_vma_offset_unlock_lookup(&bdev->vma_manager);
+
+	if (!bo)
+		pr_err("Could not find buffer object to map\n");
+
+	return bo;
+}
+
 int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
 		struct ttm_bo_device *bdev)
 {
@@ -256,17 +250,9 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
 	struct ttm_buffer_object *bo;
 	int ret;
 
-	read_lock(&bdev->vm_lock);
-	bo = ttm_bo_vm_lookup_rb(bdev, vma->vm_pgoff,
-				 vma_pages(vma));
-	if (likely(bo != NULL) && !kref_get_unless_zero(&bo->kref))
-		bo = NULL;
-	read_unlock(&bdev->vm_lock);
-
-	if (unlikely(bo == NULL)) {
-		pr_err("Could not find buffer object to map\n");
+	bo = ttm_bo_vm_lookup(bdev, vma->vm_pgoff, vma_pages(vma));
+	if (unlikely(!bo))
 		return -EINVAL;
-	}
 
 	driver = bo->bdev->driver;
 	if (unlikely(!driver->verify_access)) {
@@ -324,12 +310,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
 	bool no_wait = false;
 	bool dummy;
 
-	read_lock(&bdev->vm_lock);
-	bo = ttm_bo_vm_lookup_rb(bdev, dev_offset, 1);
-	if (likely(bo != NULL))
-		ttm_bo_reference(bo);
-	read_unlock(&bdev->vm_lock);
-
+	bo = ttm_bo_vm_lookup(bdev, dev_offset, 1);
 	if (unlikely(bo == NULL))
 		return -EFAULT;
 
@@ -343,7 +324,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
 	if (unlikely(ret != 0))
 		goto out_unref;
 
-	kmap_offset = dev_offset - bo->vm_node->start;
+	kmap_offset = dev_offset - drm_vma_node_start(&bo->vma_node);
 	if (unlikely(kmap_offset >= bo->num_pages)) {
 		ret = -EFBIG;
 		goto out_unref;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 7953d1f..0e67cf4 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -500,7 +500,7 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
 		goto out_no_dmabuf;
 
 	rep->handle = handle;
-	rep->map_handle = dma_buf->base.addr_space_offset;
+	rep->map_handle = drm_vma_node_offset_addr(&dma_buf->base.vma_node);
 	rep->cur_gmr_id = handle;
 	rep->cur_gmr_offset = 0;
 
@@ -834,7 +834,7 @@ int vmw_dumb_map_offset(struct drm_file *file_priv,
 	if (ret != 0)
 		return -EINVAL;
 
-	*offset = out_buf->base.addr_space_offset;
+	*offset = drm_vma_node_offset_addr(&out_buf->base.vma_node);
 	vmw_dmabuf_unreference(&out_buf);
 	return 0;
 }
diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
index 8a6aa56..751eaff 100644
--- a/include/drm/ttm/ttm_bo_api.h
+++ b/include/drm/ttm/ttm_bo_api.h
@@ -32,12 +32,12 @@
 #define _TTM_BO_API_H_
 
 #include <drm/drm_hashtab.h>
+#include <drm/drm_vma_manager.h>
 #include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/wait.h>
 #include <linux/mutex.h>
 #include <linux/mm.h>
-#include <linux/rbtree.h>
 #include <linux/bitmap.h>
 #include <linux/reservation.h>
 
@@ -145,7 +145,6 @@ struct ttm_tt;
  * @type: The bo type.
  * @destroy: Destruction function. If NULL, kfree is used.
  * @num_pages: Actual number of pages.
- * @addr_space_offset: Address space offset.
  * @acc_size: Accounted size for this object.
  * @kref: Reference count of this buffer object. When this refcount reaches
  * zero, the object is put on the delayed delete list.
@@ -166,8 +165,7 @@ struct ttm_tt;
  * @swap: List head for swap LRU list.
  * @sync_obj: Pointer to a synchronization object.
  * @priv_flags: Flags describing buffer object internal state.
- * @vm_rb: Rb node for the vm rb tree.
- * @vm_node: Address space manager node.
+ * @vma_node: Address space manager node.
  * @offset: The current GPU offset, which can have different meanings
  * depending on the memory type. For SYSTEM type memory, it should be 0.
  * @cur_placement: Hint of current placement.
@@ -194,7 +192,6 @@ struct ttm_buffer_object {
 	enum ttm_bo_type type;
 	void (*destroy) (struct ttm_buffer_object *);
 	unsigned long num_pages;
-	uint64_t addr_space_offset;
 	size_t acc_size;
 
 	/**
@@ -238,13 +235,7 @@ struct ttm_buffer_object {
 	void *sync_obj;
 	unsigned long priv_flags;
 
-	/**
-	 * Members protected by the bdev::vm_lock
-	 */
-
-	struct rb_node vm_rb;
-	struct drm_mm_node *vm_node;
-
+	struct drm_vma_offset_node vma_node;
 
 	/**
 	 * Special members that are protected by the reserve lock
diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
index 984fc2d..8639c85 100644
--- a/include/drm/ttm/ttm_bo_driver.h
+++ b/include/drm/ttm/ttm_bo_driver.h
@@ -36,6 +36,7 @@
 #include <ttm/ttm_placement.h>
 #include <drm/drm_mm.h>
 #include <drm/drm_global.h>
+#include <drm/drm_vma_manager.h>
 #include <linux/workqueue.h>
 #include <linux/fs.h>
 #include <linux/spinlock.h>
@@ -519,7 +520,7 @@ struct ttm_bo_global {
  * @man: An array of mem_type_managers.
  * @fence_lock: Protects the synchronizing members on *all* bos belonging
  * to this device.
- * @addr_space_mm: Range manager for the device address space.
+ * @vma_manager: Address space manager
  * lru_lock: Spinlock that protects the buffer+device lru lists and
  * ddestroy lists.
  * @val_seq: Current validation sequence.
@@ -537,14 +538,13 @@ struct ttm_bo_device {
 	struct list_head device_list;
 	struct ttm_bo_global *glob;
 	struct ttm_bo_driver *driver;
-	rwlock_t vm_lock;
 	struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES];
 	spinlock_t fence_lock;
+
 	/*
-	 * Protected by the vm lock.
+	 * Protected by internal locks.
 	 */
-	struct rb_root addr_space_rb;
-	struct drm_mm addr_space_mm;
+	struct drm_vma_offset_manager vma_manager;
 
 	/*
 	 * Protected by the global:lru lock.
-- 
1.8.3.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v5 4/4] drm/vma: provide drm_vma_node_unmap() helper
  2013-07-23 12:47   ` [PATCH v4 4/4] drm/vma: provide drm_vma_node_unmap() helper David Herrmann
  2013-07-24 15:58     ` Daniel Vetter
@ 2013-07-24 19:10     ` David Herrmann
  1 sibling, 0 replies; 31+ messages in thread
From: David Herrmann @ 2013-07-24 19:10 UTC (permalink / raw)
  To: dri-devel; +Cc: Dave Airlie, Thomas Hellstrom

Instead of unmapping the nodes in TTM and GEM users manually, we provide
a generic wrapper which does the correct thing for all vma-nodes.

v2: remove bdev->dev_mapping test in ttm_bo_unmap_virtual_unlocked() as
ttm_mem_io_free_vm() does nothing in that case (io_reserved_vm is 0).
v4: Fix docbook comments
v5: use drm_vma_node_size()

Cc: Dave Airlie <airlied@redhat.com>
Cc: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Cc: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---
 drivers/gpu/drm/i915/i915_gem.c |  6 +-----
 drivers/gpu/drm/ttm/ttm_bo.c    | 11 +----------
 include/drm/drm_vma_manager.h   | 22 ++++++++++++++++++++++
 3 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 53f81b3..8673a00 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1427,11 +1427,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
 	if (!obj->fault_mappable)
 		return;
 
-	if (obj->base.dev->dev_mapping)
-		unmap_mapping_range(obj->base.dev->dev_mapping,
-				    (loff_t)drm_vma_node_offset_addr(&obj->base.vma_node),
-				    obj->base.size, 1);
-
+	drm_vma_node_unmap(&obj->base.vma_node, obj->base.dev->dev_mapping);
 	obj->fault_mappable = false;
 }
 
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 3dc08b6..050edfa 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1488,17 +1488,8 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
 void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo)
 {
 	struct ttm_bo_device *bdev = bo->bdev;
-	loff_t offset, holelen;
 
-	if (!bdev->dev_mapping)
-		return;
-
-	if (drm_vma_node_has_offset(&bo->vma_node)) {
-		offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node);
-		holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT;
-
-		unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1);
-	}
+	drm_vma_node_unmap(&bo->vma_node, bdev->dev_mapping);
 	ttm_mem_io_free_vm(bo);
 }
 
diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h
index 7ee8c4b..22eedac 100644
--- a/include/drm/drm_vma_manager.h
+++ b/include/drm/drm_vma_manager.h
@@ -24,6 +24,7 @@
  */
 
 #include <drm/drm_mm.h>
+#include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/rbtree.h>
 #include <linux/spinlock.h>
@@ -199,4 +200,25 @@ static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
 	return ((__u64)node->vm_node.start) << PAGE_SHIFT;
 }
 
+/**
+ * drm_vma_node_unmap() - Unmap offset node
+ * @node: Offset node
+ * @file_mapping: Address space to unmap @node from
+ *
+ * Unmap all userspace mappings for a given offset node. The mappings must be
+ * associated with the @file_mapping address-space. If no offset exists or
+ * the address-space is invalid, nothing is done.
+ *
+ * This call is unlocked. The caller must guarantee that drm_vma_offset_remove()
+ * is not called on this node concurrently.
+ */
+static inline void drm_vma_node_unmap(struct drm_vma_offset_node *node,
+				      struct address_space *file_mapping)
+{
+	if (file_mapping && drm_vma_node_has_offset(node))
+		unmap_mapping_range(file_mapping,
+				    drm_vma_node_offset_addr(node),
+				    drm_vma_node_size(node) << PAGE_SHIFT, 1);
+}
+
 #endif /* __DRM_VMA_MANAGER_H__ */
-- 
1.8.3.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2013-07-24 19:10 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-07-17 18:14 [PATCH v3 0/4] Unified VMA Offset Manager v3 David Herrmann
2013-07-17 18:14 ` [PATCH v3 1/4] drm: add unified vma offset manager David Herrmann
2013-07-17 18:14 ` [PATCH v3 2/4] drm/gem: convert to new unified vma manager David Herrmann
2013-07-18 19:01   ` Patrik Jakobsson
2013-07-17 18:14 ` [PATCH v3 3/4] drm/ttm: convert to unified vma offset manager David Herrmann
2013-07-18  8:53   ` Thomas Hellstrom
2013-07-18 11:02     ` Daniel Vetter
2013-07-18 11:07     ` David Herrmann
2013-07-18 11:24       ` Thomas Hellstrom
2013-07-18 20:54         ` David Herrmann
2013-07-19  2:03           ` Jerome Glisse
2013-07-19  7:24           ` Maarten Lankhorst
2013-07-19  9:13           ` Thomas Hellstrom
     [not found]             ` <CANq1E4SSLtezptzo18TD3fLG_zoRwR17kMxEi96-e957F5wL1A@mail.gmail.com>
2013-07-22 10:55               ` David Herrmann
2013-07-22 11:45                 ` Thomas Hellstrom
2013-07-17 18:14 ` [PATCH v3 4/4] drm/vma: provide drm_vma_node_unmap() helper David Herrmann
2013-07-23 12:47 ` [PATCH v4 0/4] Unified VMA Offset Manager David Herrmann
2013-07-23 12:47   ` [PATCH v4 1/4] drm: add unified vma offset manager David Herrmann
2013-07-24 15:35     ` Daniel Vetter
2013-07-24 16:10       ` David Herrmann
2013-07-24 19:06     ` [PATCH v5 " David Herrmann
2013-07-23 12:47   ` [PATCH v4 2/4] drm/gem: convert to new unified vma manager David Herrmann
2013-07-24 15:52     ` Daniel Vetter
2013-07-24 16:27       ` David Herrmann
2013-07-24 19:07     ` [PATCH v5 " David Herrmann
2013-07-23 12:47   ` [PATCH v4 3/4] drm/ttm: convert to unified vma offset manager David Herrmann
2013-07-24 15:57     ` Daniel Vetter
2013-07-24 19:08     ` [PATCH v5 " David Herrmann
2013-07-23 12:47   ` [PATCH v4 4/4] drm/vma: provide drm_vma_node_unmap() helper David Herrmann
2013-07-24 15:58     ` Daniel Vetter
2013-07-24 19:10     ` [PATCH v5 " David Herrmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.