aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_bufs.c
diff options
context:
space:
mode:
authorDavid Miller <davem@davemloft.net>2009-02-18 18:41:02 -0500
committerDave Airlie <airlied@redhat.com>2009-03-13 00:24:03 -0400
commitf1a2a9b6189f9f5c27672d4d32fec9492c6486b2 (patch)
treea85f1d2e84afd78a6dd188b048f7dfee9607e510 /drivers/gpu/drm/drm_bufs.c
parentd30333bbabb4a2cfad1f1a45c48a4e4d0065c1f6 (diff)
drm: Preserve SHMLBA bits in hash key for _DRM_SHM mappings.
Platforms such as sparc64 have D-cache aliasing issues. We cannot allow virtual mappings in different contexts to be such that two cache lines can be loaded for the same backing data. Updates to one cache line won't be seen by accesses to the other cache line. Code in sparc64 and other architectures solve this problem by making sure that all userland mappings of MAP_SHARED objects have the same virtual address base. They implement this by keying off of the page offset, and using that to choose a suitably consistent virtual address for mmap() requests. Making things even worse, getting this wrong on sparc64 can result in hangs during DRM lock acquisition. This is because, at least on UltraSPARC-III, normal loads consult the D-cache but atomics such as 'cas' (which is what cmpxchg() is implement using) only consult the L2 cache. So if a D-cache alias is inserted, the load can see different data than the atomic, and we'll loop forever because the atomic compare-and-exchange will never complete successfully. So to make this all work properly, we need to make sure that the hash address computed by drm_map_handle() preserves the SHMLBA relevant bits, and that's what this patch does for _DRM_SHM mappings. As a historical note, many years ago this bug didn't exist because we used to just use the low 32-bits of the address as the hash and just hope for the best. This preserved the SHMLBA bits properly. But when the hashtab code was added to DRM, this was no longer the case. Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/drm_bufs.c')
-rw-r--r--drivers/gpu/drm/drm_bufs.c35
1 files changed, 31 insertions, 4 deletions
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index cddea1a2472c..6d80d17f1e96 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -34,6 +34,8 @@
34 */ 34 */
35 35
36#include <linux/vmalloc.h> 36#include <linux/vmalloc.h>
37#include <linux/log2.h>
38#include <asm/shmparam.h>
37#include "drmP.h" 39#include "drmP.h"
38 40
39resource_size_t drm_get_resource_start(struct drm_device *dev, unsigned int resource) 41resource_size_t drm_get_resource_start(struct drm_device *dev, unsigned int resource)
@@ -83,9 +85,11 @@ static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
83} 85}
84 86
85static int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash, 87static int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash,
86 unsigned long user_token, int hashed_handle) 88 unsigned long user_token, int hashed_handle, int shm)
87{ 89{
88 int use_hashed_handle; 90 int use_hashed_handle, shift;
91 unsigned long add;
92
89#if (BITS_PER_LONG == 64) 93#if (BITS_PER_LONG == 64)
90 use_hashed_handle = ((user_token & 0xFFFFFFFF00000000UL) || hashed_handle); 94 use_hashed_handle = ((user_token & 0xFFFFFFFF00000000UL) || hashed_handle);
91#elif (BITS_PER_LONG == 32) 95#elif (BITS_PER_LONG == 32)
@@ -101,9 +105,31 @@ static int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash,
101 if (ret != -EINVAL) 105 if (ret != -EINVAL)
102 return ret; 106 return ret;
103 } 107 }
108
109 shift = 0;
110 add = DRM_MAP_HASH_OFFSET >> PAGE_SHIFT;
111 if (shm && (SHMLBA > PAGE_SIZE)) {
112 int bits = ilog2(SHMLBA >> PAGE_SHIFT) + 1;
113
114 /* For shared memory, we have to preserve the SHMLBA
115 * bits of the eventual vma->vm_pgoff value during
116 * mmap(). Otherwise we run into cache aliasing problems
117 * on some platforms. On these platforms, the pgoff of
118 * a mmap() request is used to pick a suitable virtual
119 * address for the mmap() region such that it will not
120 * cause cache aliasing problems.
121 *
122 * Therefore, make sure the SHMLBA relevant bits of the
123 * hash value we use are equal to those in the original
124 * kernel virtual address.
125 */
126 shift = bits;
127 add |= ((user_token >> PAGE_SHIFT) & ((1UL << bits) - 1UL));
128 }
129
104 return drm_ht_just_insert_please(&dev->map_hash, hash, 130 return drm_ht_just_insert_please(&dev->map_hash, hash,
105 user_token, 32 - PAGE_SHIFT - 3, 131 user_token, 32 - PAGE_SHIFT - 3,
106 0, DRM_MAP_HASH_OFFSET >> PAGE_SHIFT); 132 shift, add);
107} 133}
108 134
109/** 135/**
@@ -323,7 +349,8 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
323 /* We do it here so that dev->struct_mutex protects the increment */ 349 /* We do it here so that dev->struct_mutex protects the increment */
324 user_token = (map->type == _DRM_SHM) ? (unsigned long)map->handle : 350 user_token = (map->type == _DRM_SHM) ? (unsigned long)map->handle :
325 map->offset; 351 map->offset;
326 ret = drm_map_handle(dev, &list->hash, user_token, 0); 352 ret = drm_map_handle(dev, &list->hash, user_token, 0,
353 (map->type == _DRM_SHM));
327 if (ret) { 354 if (ret) {
328 if (map->type == _DRM_REGISTERS) 355 if (map->type == _DRM_REGISTERS)
329 iounmap(map->handle); 356 iounmap(map->handle);