drm: Make drm_local_map use a resource_size_t offset
[deliverable/linux.git] / drivers / gpu / drm / drm_bufs.c
index bde64b84166e1d783a2c5a8ece1816c483e76f0b..cddea1a2472c8abd41370b38f0759a8a3d86a0cf 100644 (file)
 #include <linux/vmalloc.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,15 +50,33 @@ 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 && 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;
@@ -89,24 +107,19 @@ static int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash,
 }
 
 /**
- * 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 +142,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;
        }
@@ -210,12 +223,12 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
                map->offset = (unsigned long)map->handle;
                if (map->flags & _DRM_CONTAINS_LOCK) {
                        /* Prevent a 2nd X Server from creating a 2nd lock */
-                       if (dev->lock.hw_lock != NULL) {
+                       if (dev->primary->master->lock.hw_lock != NULL) {
                                vfree(map->handle);
                                drm_free(map, sizeof(*map), DRM_MEM_MAPS);
                                return -EBUSY;
                        }
-                       dev->sigdata.lock = dev->lock.hw_lock = map->handle;    /* Pointer to lock */
+                       dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle;   /* Pointer to lock */
                }
                break;
        case _DRM_AGP: {
@@ -259,9 +272,13 @@ 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:
+               DRM_ERROR("tried to rmmap GEM object\n");
+               break;
        }
        case _DRM_SCATTER_GATHER:
                if (!dev->sg) {
@@ -319,13 +336,14 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
        list->user_token = list->hash.key << PAGE_SHIFT;
        mutex_unlock(&dev->struct_mutex);
 
+       list->master = dev->primary->master;
        *maplist = list;
        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;
@@ -338,6 +356,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)
 {
@@ -345,7 +374,7 @@ int drm_addmap_ioctl(struct drm_device *dev, void *data,
        struct drm_map_list *maplist;
        int err;
 
-       if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP))
+       if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP || map->type == _DRM_SHM))
                return -EPERM;
 
        err = drm_addmap_core(dev, map->offset, map->size, map->type,
@@ -363,27 +392,23 @@ 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;
        int found = 0;
+       struct drm_master *master;
 
        /* Find the list entry for the map and remove it */
        list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
                if (r_list->map == map) {
+                       master = r_list->master;
                        list_del(&r_list->head);
                        drm_ht_remove_key(&dev->map_hash,
                                          r_list->user_token >> PAGE_SHIFT);
@@ -409,6 +434,13 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
                break;
        case _DRM_SHM:
                vfree(map->handle);
+               if (master) {
+                       if (dev->sigdata.lock == master->lock.hw_lock)
+                               dev->sigdata.lock = NULL;
+                       master->lock.hw_lock = NULL;   /* SHM removed */
+                       master->lock.file_priv = NULL;
+                       wake_up_interruptible_all(&master->lock.lock_queue);
+               }
                break;
        case _DRM_AGP:
        case _DRM_SCATTER_GATHER:
@@ -419,13 +451,17 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
                dmah.size = map->size;
                __drm_pci_free(dev, &dmah);
                break;
+       case _DRM_GEM:
+               DRM_ERROR("tried to rmmap GEM object\n");
+               break;
        }
        drm_free(map, sizeof(*map), DRM_MEM_MAPS);
 
        return 0;
 }
+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;
 
@@ -445,12 +481,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;
 
@@ -1517,7 +1559,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) {
This page took 0.027273 seconds and 5 git commands to generate.