summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_bufs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_bufs.c')
-rw-r--r--drivers/gpu/drm/drm_bufs.c122
1 files changed, 87 insertions, 35 deletions
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index 12715d3c078d..6d80d17f1e96 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -34,15 +34,17 @@
*/
#include <linux/vmalloc.h>
+#include <linux/log2.h>
+#include <asm/shmparam.h>
#include "drmP.h"
-unsigned long drm_get_resource_start(struct drm_device *dev, unsigned int resource)
+resource_size_t drm_get_resource_start(struct drm_device *dev, unsigned int resource)
{
return pci_resource_start(dev->pdev, resource);
}
EXPORT_SYMBOL(drm_get_resource_start);
-unsigned long drm_get_resource_len(struct drm_device *dev, unsigned int resource)
+resource_size_t drm_get_resource_len(struct drm_device *dev, unsigned int resource)
{
return pci_resource_len(dev->pdev, resource);
}
@@ -50,24 +52,44 @@ unsigned long drm_get_resource_len(struct drm_device *dev, unsigned int resource
EXPORT_SYMBOL(drm_get_resource_len);
static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
- drm_local_map_t *map)
+ struct drm_local_map *map)
{
struct drm_map_list *entry;
list_for_each_entry(entry, &dev->maplist, head) {
- if (entry->map && (entry->master == dev->primary->master) && (map->type == entry->map->type) &&
- ((entry->map->offset == map->offset) ||
- ((map->type == _DRM_SHM) && (map->flags&_DRM_CONTAINS_LOCK)))) {
+ /*
+ * Because the kernel-userspace ABI is fixed at a 32-bit offset
+ * while PCI resources may live above that, we ignore the map
+ * offset for maps of type _DRM_FRAMEBUFFER or _DRM_REGISTERS.
+ * It is assumed that each driver will have only one resource of
+ * each type.
+ */
+ if (!entry->map ||
+ map->type != entry->map->type ||
+ entry->master != dev->primary->master)
+ continue;
+ switch (map->type) {
+ case _DRM_SHM:
+ if (map->flags != _DRM_CONTAINS_LOCK)
+ break;
+ case _DRM_REGISTERS:
+ case _DRM_FRAME_BUFFER:
return entry;
+ default: /* Make gcc happy */
+ ;
}
+ if (entry->map->offset == map->offset)
+ return entry;
}
return NULL;
}
static int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash,
- unsigned long user_token, int hashed_handle)
+ unsigned long user_token, int hashed_handle, int shm)
{
- int use_hashed_handle;
+ int use_hashed_handle, shift;
+ unsigned long add;
+
#if (BITS_PER_LONG == 64)
use_hashed_handle = ((user_token & 0xFFFFFFFF00000000UL) || hashed_handle);
#elif (BITS_PER_LONG == 32)
@@ -83,30 +105,47 @@ static int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash,
if (ret != -EINVAL)
return ret;
}
+
+ shift = 0;
+ add = DRM_MAP_HASH_OFFSET >> PAGE_SHIFT;
+ if (shm && (SHMLBA > PAGE_SIZE)) {
+ int bits = ilog2(SHMLBA >> PAGE_SHIFT) + 1;
+
+ /* For shared memory, we have to preserve the SHMLBA
+ * bits of the eventual vma->vm_pgoff value during
+ * mmap(). Otherwise we run into cache aliasing problems
+ * on some platforms. On these platforms, the pgoff of
+ * a mmap() request is used to pick a suitable virtual
+ * address for the mmap() region such that it will not
+ * cause cache aliasing problems.
+ *
+ * Therefore, make sure the SHMLBA relevant bits of the
+ * hash value we use are equal to those in the original
+ * kernel virtual address.
+ */
+ shift = bits;
+ add |= ((user_token >> PAGE_SHIFT) & ((1UL << bits) - 1UL));
+ }
+
return drm_ht_just_insert_please(&dev->map_hash, hash,
user_token, 32 - PAGE_SHIFT - 3,
- 0, DRM_MAP_HASH_OFFSET >> PAGE_SHIFT);
+ shift, add);
}
/**
- * Ioctl to specify a range of memory that is available for mapping by a non-root process.
- *
- * \param inode device inode.
- * \param file_priv DRM file private.
- * \param cmd command.
- * \param arg pointer to a drm_map structure.
- * \return zero on success or a negative value on error.
+ * Core function to create a range of memory available for mapping by a
+ * non-root process.
*
* Adjusts the memory offset to its absolute value according to the mapping
* type. Adds the map to the map list drm_device::maplist. Adds MTRR's where
* applicable and if supported by the kernel.
*/
-static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
+static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
unsigned int size, enum drm_map_type type,
enum drm_map_flags flags,
struct drm_map_list ** maplist)
{
- struct drm_map *map;
+ struct drm_local_map *map;
struct drm_map_list *list;
drm_dma_handle_t *dmah;
unsigned long user_token;
@@ -129,9 +168,9 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
return -EINVAL;
}
- DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n",
- map->offset, map->size, map->type);
- if ((map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK))) {
+ DRM_DEBUG("offset = 0x%08llx, size = 0x%08lx, type = %d\n",
+ (unsigned long long)map->offset, map->size, map->type);
+ if ((map->offset & (~(resource_size_t)PAGE_MASK)) || (map->size & (~PAGE_MASK))) {
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
return -EINVAL;
}
@@ -259,7 +298,8 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
return -EPERM;
}
- DRM_DEBUG("AGP offset = 0x%08lx, size = 0x%08lx\n", map->offset, map->size);
+ DRM_DEBUG("AGP offset = 0x%08llx, size = 0x%08lx\n",
+ (unsigned long long)map->offset, map->size);
break;
case _DRM_GEM:
@@ -309,7 +349,8 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
/* We do it here so that dev->struct_mutex protects the increment */
user_token = (map->type == _DRM_SHM) ? (unsigned long)map->handle :
map->offset;
- ret = drm_map_handle(dev, &list->hash, user_token, 0);
+ ret = drm_map_handle(dev, &list->hash, user_token, 0,
+ (map->type == _DRM_SHM));
if (ret) {
if (map->type == _DRM_REGISTERS)
iounmap(map->handle);
@@ -327,9 +368,9 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
return 0;
}
-int drm_addmap(struct drm_device * dev, unsigned int offset,
+int drm_addmap(struct drm_device * dev, resource_size_t offset,
unsigned int size, enum drm_map_type type,
- enum drm_map_flags flags, drm_local_map_t ** map_ptr)
+ enum drm_map_flags flags, struct drm_local_map ** map_ptr)
{
struct drm_map_list *list;
int rc;
@@ -342,6 +383,17 @@ int drm_addmap(struct drm_device * dev, unsigned int offset,
EXPORT_SYMBOL(drm_addmap);
+/**
+ * Ioctl to specify a range of memory that is available for mapping by a
+ * non-root process.
+ *
+ * \param inode device inode.
+ * \param file_priv DRM file private.
+ * \param cmd command.
+ * \param arg pointer to a drm_map structure.
+ * \return zero on success or a negative value on error.
+ *
+ */
int drm_addmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -367,19 +419,13 @@ int drm_addmap_ioctl(struct drm_device *dev, void *data,
* Remove a map private from list and deallocate resources if the mapping
* isn't in use.
*
- * \param inode device inode.
- * \param file_priv DRM file private.
- * \param cmd command.
- * \param arg pointer to a struct drm_map structure.
- * \return zero on success or a negative value on error.
- *
* Searches the map on drm_device::maplist, removes it from the list, see if
* its being used, and free any associate resource (such as MTRR's) if it's not
* being on use.
*
* \sa drm_addmap
*/
-int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
+int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map)
{
struct drm_map_list *r_list = NULL, *list_t;
drm_dma_handle_t dmah;
@@ -442,7 +488,7 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
}
EXPORT_SYMBOL(drm_rmmap_locked);
-int drm_rmmap(struct drm_device *dev, drm_local_map_t *map)
+int drm_rmmap(struct drm_device *dev, struct drm_local_map *map)
{
int ret;
@@ -462,12 +508,18 @@ EXPORT_SYMBOL(drm_rmmap);
* One use case might be after addmap is allowed for normal users for SHM and
* gets used by drivers that the server doesn't need to care about. This seems
* unlikely.
+ *
+ * \param inode device inode.
+ * \param file_priv DRM file private.
+ * \param cmd command.
+ * \param arg pointer to a struct drm_map structure.
+ * \return zero on success or a negative value on error.
*/
int drm_rmmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_map *request = data;
- drm_local_map_t *map = NULL;
+ struct drm_local_map *map = NULL;
struct drm_map_list *r_list;
int ret;
@@ -1534,7 +1586,7 @@ int drm_mapbufs(struct drm_device *dev, void *data,
&& (dma->flags & _DRM_DMA_USE_SG))
|| (drm_core_check_feature(dev, DRIVER_FB_DMA)
&& (dma->flags & _DRM_DMA_USE_FB))) {
- struct drm_map *map = dev->agp_buffer_map;
+ struct drm_local_map *map = dev->agp_buffer_map;
unsigned long token = dev->agp_buffer_token;
if (!map) {