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