^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) * \file drm_scatter.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * IOCTLs to manage scatter/gather memory
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * \author Gareth Hughes <gareth@valinux.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * Created: Mon Dec 18 23:20:54 2000 by gareth@valinux.com
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * All Rights Reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * Permission is hereby granted, free of charge, to any person obtaining a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * copy of this software and associated documentation files (the "Software"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * to deal in the Software without restriction, including without limitation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) * the rights to use, copy, modify, merge, publish, distribute, sublicense,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * and/or sell copies of the Software, and to permit persons to whom the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * Software is furnished to do so, subject to the following conditions:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * The above copyright notice and this permission notice (including the next
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * paragraph) shall be included in all copies or substantial portions of the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * Software.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * DEALINGS IN THE SOFTWARE.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #include <linux/mm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) #include <linux/vmalloc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) #include <drm/drm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) #include <drm/drm_drv.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) #include <drm/drm_print.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #include "drm_legacy.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #define DEBUG_SCATTER 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) static void drm_sg_cleanup(struct drm_sg_mem * entry)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) struct page *page;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) for (i = 0; i < entry->pages; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) page = entry->pagelist[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) if (page)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) ClearPageReserved(page);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) vfree(entry->virtual);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) kfree(entry->busaddr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) kfree(entry->pagelist);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) kfree(entry);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) void drm_legacy_sg_cleanup(struct drm_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) drm_core_check_feature(dev, DRIVER_LEGACY)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) drm_sg_cleanup(dev->sg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) dev->sg = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) #ifdef _LP64
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) # define ScatterHandle(x) (unsigned int)((x >> 32) + (x & ((1L << 32) - 1)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) # define ScatterHandle(x) (unsigned int)(x)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) int drm_legacy_sg_alloc(struct drm_device *dev, void *data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) struct drm_file *file_priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) struct drm_scatter_gather *request = data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) struct drm_sg_mem *entry;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) unsigned long pages, i, j;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) DRM_DEBUG("\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) if (!drm_core_check_feature(dev, DRIVER_LEGACY))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) return -EOPNOTSUPP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) if (!drm_core_check_feature(dev, DRIVER_SG))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) return -EOPNOTSUPP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) if (request->size > SIZE_MAX - PAGE_SIZE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) if (dev->sg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) entry = kzalloc(sizeof(*entry), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) if (!entry)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) DRM_DEBUG("size=%ld pages=%ld\n", request->size, pages);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) entry->pages = pages;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) entry->pagelist = kcalloc(pages, sizeof(*entry->pagelist), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) if (!entry->pagelist) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) kfree(entry);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) entry->busaddr = kcalloc(pages, sizeof(*entry->busaddr), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) if (!entry->busaddr) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) kfree(entry->pagelist);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) kfree(entry);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) entry->virtual = vmalloc_32(pages << PAGE_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) if (!entry->virtual) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) kfree(entry->busaddr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) kfree(entry->pagelist);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) kfree(entry);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) /* This also forces the mapping of COW pages, so our page list
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) * will be valid. Please don't remove it...
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) memset(entry->virtual, 0, pages << PAGE_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) entry->handle = ScatterHandle((unsigned long)entry->virtual);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) DRM_DEBUG("handle = %08lx\n", entry->handle);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) DRM_DEBUG("virtual = %p\n", entry->virtual);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) for (i = (unsigned long)entry->virtual, j = 0; j < pages;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) i += PAGE_SIZE, j++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) entry->pagelist[j] = vmalloc_to_page((void *)i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) if (!entry->pagelist[j])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) goto failed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) SetPageReserved(entry->pagelist[j]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) request->handle = entry->handle;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) dev->sg = entry;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) #if DEBUG_SCATTER
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) /* Verify that each page points to its virtual address, and vice
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) * versa.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) int error = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) for (i = 0; i < pages; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) unsigned long *tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) tmp = page_address(entry->pagelist[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) for (j = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) j < PAGE_SIZE / sizeof(unsigned long);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) j++, tmp++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) *tmp = 0xcafebabe;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) tmp = (unsigned long *)((u8 *) entry->virtual +
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) (PAGE_SIZE * i));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) for (j = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) j < PAGE_SIZE / sizeof(unsigned long);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) j++, tmp++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) if (*tmp != 0xcafebabe && error == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) error = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) DRM_ERROR("Scatter allocation error, "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) "pagelist does not match "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) "virtual mapping\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) tmp = page_address(entry->pagelist[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) for (j = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) j < PAGE_SIZE / sizeof(unsigned long);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) j++, tmp++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) *tmp = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) if (error == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) DRM_ERROR("Scatter allocation matches pagelist\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) failed:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) drm_sg_cleanup(entry);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) int drm_legacy_sg_free(struct drm_device *dev, void *data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) struct drm_file *file_priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) struct drm_scatter_gather *request = data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) struct drm_sg_mem *entry;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) if (!drm_core_check_feature(dev, DRIVER_LEGACY))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) return -EOPNOTSUPP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) if (!drm_core_check_feature(dev, DRIVER_SG))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) return -EOPNOTSUPP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) entry = dev->sg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) dev->sg = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) if (!entry || entry->handle != request->handle)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) DRM_DEBUG("virtual = %p\n", entry->virtual);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) drm_sg_cleanup(entry);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) }