Branch data Line data Source code
1 : : /*
2 : : * SPDX-License-Identifier: MIT
3 : : *
4 : : * Copyright © 2014-2016 Intel Corporation
5 : : */
6 : :
7 : : #include <linux/highmem.h>
8 : : #include <linux/shmem_fs.h>
9 : : #include <linux/swap.h>
10 : :
11 : : #include <drm/drm.h> /* for drm_legacy.h! */
12 : : #include <drm/drm_cache.h>
13 : : #include <drm/drm_legacy.h> /* for drm_pci.h! */
14 : : #include <drm/drm_pci.h>
15 : :
16 : : #include "gt/intel_gt.h"
17 : : #include "i915_drv.h"
18 : : #include "i915_gem_object.h"
19 : : #include "i915_gem_region.h"
20 : : #include "i915_scatterlist.h"
21 : :
22 : 0 : static int i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj)
23 : : {
24 : 0 : struct address_space *mapping = obj->base.filp->f_mapping;
25 : 0 : struct scatterlist *sg;
26 : 0 : struct sg_table *st;
27 : 0 : dma_addr_t dma;
28 : 0 : void *vaddr;
29 : 0 : void *dst;
30 : 0 : int i;
31 : :
32 [ # # # # : 0 : if (WARN_ON(i915_gem_object_needs_bit17_swizzle(obj)))
# # ]
33 : : return -EINVAL;
34 : :
35 : : /*
36 : : * Always aligning to the object size, allows a single allocation
37 : : * to handle all possible callers, and given typical object sizes,
38 : : * the alignment of the buddy allocation will naturally match.
39 : : */
40 : 0 : vaddr = dma_alloc_coherent(&obj->base.dev->pdev->dev,
41 [ # # # # : 0 : roundup_pow_of_two(obj->base.size),
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
42 : : &dma, GFP_KERNEL);
43 [ # # ]: 0 : if (!vaddr)
44 : : return -ENOMEM;
45 : :
46 : 0 : st = kmalloc(sizeof(*st), GFP_KERNEL);
47 [ # # ]: 0 : if (!st)
48 : 0 : goto err_pci;
49 : :
50 [ # # ]: 0 : if (sg_alloc_table(st, 1, GFP_KERNEL))
51 : 0 : goto err_st;
52 : :
53 : 0 : sg = st->sgl;
54 : 0 : sg->offset = 0;
55 : 0 : sg->length = obj->base.size;
56 : :
57 [ # # ]: 0 : sg_assign_page(sg, (struct page *)vaddr);
58 : 0 : sg_dma_address(sg) = dma;
59 : 0 : sg_dma_len(sg) = obj->base.size;
60 : :
61 : 0 : dst = vaddr;
62 [ # # ]: 0 : for (i = 0; i < obj->base.size / PAGE_SIZE; i++) {
63 : 0 : struct page *page;
64 : 0 : void *src;
65 : :
66 : 0 : page = shmem_read_mapping_page(mapping, i);
67 [ # # ]: 0 : if (IS_ERR(page))
68 : 0 : goto err_st;
69 : :
70 : 0 : src = kmap_atomic(page);
71 : 0 : memcpy(dst, src, PAGE_SIZE);
72 : 0 : drm_clflush_virt_range(dst, PAGE_SIZE);
73 : 0 : kunmap_atomic(src);
74 : :
75 : 0 : put_page(page);
76 : 0 : dst += PAGE_SIZE;
77 : : }
78 : :
79 : 0 : intel_gt_chipset_flush(&to_i915(obj->base.dev)->gt);
80 : :
81 : 0 : __i915_gem_object_set_pages(obj, st, sg->length);
82 : :
83 : 0 : return 0;
84 : :
85 : 0 : err_st:
86 : 0 : kfree(st);
87 : 0 : err_pci:
88 : 0 : dma_free_coherent(&obj->base.dev->pdev->dev,
89 [ # # # # : 0 : roundup_pow_of_two(obj->base.size),
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
90 : : vaddr, dma);
91 : 0 : return -ENOMEM;
92 : : }
93 : :
94 : : static void
95 : 0 : i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj,
96 : : struct sg_table *pages)
97 : : {
98 : 0 : dma_addr_t dma = sg_dma_address(pages->sgl);
99 : 0 : void *vaddr = sg_page(pages->sgl);
100 : :
101 : 0 : __i915_gem_object_release_shmem(obj, pages, false);
102 : :
103 [ # # ]: 0 : if (obj->mm.dirty) {
104 : 0 : struct address_space *mapping = obj->base.filp->f_mapping;
105 : 0 : void *src = vaddr;
106 : 0 : int i;
107 : :
108 [ # # ]: 0 : for (i = 0; i < obj->base.size / PAGE_SIZE; i++) {
109 : 0 : struct page *page;
110 : 0 : char *dst;
111 : :
112 : 0 : page = shmem_read_mapping_page(mapping, i);
113 [ # # ]: 0 : if (IS_ERR(page))
114 : 0 : continue;
115 : :
116 : 0 : dst = kmap_atomic(page);
117 : 0 : drm_clflush_virt_range(src, PAGE_SIZE);
118 : 0 : memcpy(dst, src, PAGE_SIZE);
119 : 0 : kunmap_atomic(dst);
120 : :
121 : 0 : set_page_dirty(page);
122 [ # # ]: 0 : if (obj->mm.madv == I915_MADV_WILLNEED)
123 : 0 : mark_page_accessed(page);
124 : 0 : put_page(page);
125 : :
126 : 0 : src += PAGE_SIZE;
127 : : }
128 : 0 : obj->mm.dirty = false;
129 : : }
130 : :
131 : 0 : sg_free_table(pages);
132 : 0 : kfree(pages);
133 : :
134 : 0 : dma_free_coherent(&obj->base.dev->pdev->dev,
135 [ # # # # : 0 : roundup_pow_of_two(obj->base.size),
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
136 : : vaddr, dma);
137 : 0 : }
138 : :
139 : 0 : static void phys_release(struct drm_i915_gem_object *obj)
140 : : {
141 : 0 : fput(obj->base.filp);
142 : 0 : }
143 : :
144 : : static const struct drm_i915_gem_object_ops i915_gem_phys_ops = {
145 : : .get_pages = i915_gem_object_get_pages_phys,
146 : : .put_pages = i915_gem_object_put_pages_phys,
147 : :
148 : : .release = phys_release,
149 : : };
150 : :
151 : 0 : int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
152 : : {
153 : 0 : struct sg_table *pages;
154 : 0 : int err;
155 : :
156 [ # # ]: 0 : if (align > obj->base.size)
157 : : return -EINVAL;
158 : :
159 [ # # ]: 0 : if (obj->ops == &i915_gem_phys_ops)
160 : : return 0;
161 : :
162 [ # # ]: 0 : if (obj->ops != &i915_gem_shmem_ops)
163 : : return -EINVAL;
164 : :
165 : 0 : err = i915_gem_object_unbind(obj, I915_GEM_OBJECT_UNBIND_ACTIVE);
166 [ # # ]: 0 : if (err)
167 : : return err;
168 : :
169 : 0 : mutex_lock_nested(&obj->mm.lock, I915_MM_GET_PAGES);
170 : :
171 [ # # ]: 0 : if (obj->mm.madv != I915_MADV_WILLNEED) {
172 : 0 : err = -EFAULT;
173 : 0 : goto err_unlock;
174 : : }
175 : :
176 [ # # ]: 0 : if (obj->mm.quirked) {
177 : 0 : err = -EFAULT;
178 : 0 : goto err_unlock;
179 : : }
180 : :
181 [ # # ]: 0 : if (obj->mm.mapping) {
182 : 0 : err = -EBUSY;
183 : 0 : goto err_unlock;
184 : : }
185 : :
186 : 0 : pages = __i915_gem_object_unset_pages(obj);
187 : :
188 : 0 : obj->ops = &i915_gem_phys_ops;
189 : :
190 : 0 : err = ____i915_gem_object_get_pages(obj);
191 [ # # ]: 0 : if (err)
192 : 0 : goto err_xfer;
193 : :
194 : : /* Perma-pin (until release) the physical set of pages */
195 : 0 : __i915_gem_object_pin_pages(obj);
196 : :
197 [ # # # # ]: 0 : if (!IS_ERR_OR_NULL(pages)) {
198 : 0 : i915_gem_shmem_ops.put_pages(obj, pages);
199 : 0 : i915_gem_object_release_memory_region(obj);
200 : : }
201 : 0 : mutex_unlock(&obj->mm.lock);
202 : 0 : return 0;
203 : :
204 : : err_xfer:
205 : 0 : obj->ops = &i915_gem_shmem_ops;
206 [ # # # # ]: 0 : if (!IS_ERR_OR_NULL(pages)) {
207 : 0 : unsigned int sg_page_sizes = i915_sg_page_sizes(pages->sgl);
208 : :
209 : 0 : __i915_gem_object_set_pages(obj, pages, sg_page_sizes);
210 : : }
211 : 0 : err_unlock:
212 : 0 : mutex_unlock(&obj->mm.lock);
213 : 0 : return err;
214 : : }
215 : :
216 : : #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
217 : : #include "selftests/i915_gem_phys.c"
218 : : #endif
|