| @@ -1,219 +1,220 @@ | | | @@ -1,219 +1,220 @@ |
1 | /* $NetBSD: drm_scatter.c,v 1.1.2.1 2013/07/24 02:46:33 riastradh Exp $ */ | | 1 | /* $NetBSD: drm_scatter.c,v 1.1.2.2 2013/07/24 03:27:52 riastradh Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2013 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 2013 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation | | 7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Taylor R. Campbell. | | 8 | * by Taylor R. Campbell. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright | | 15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the | | 16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. | | 17 | * documentation and/or other materials provided with the distribution. |
18 | * | | 18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | | 19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | | 20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | | 21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | | 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. | | 29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | #include <sys/cdefs.h> | | 32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: drm_scatter.c,v 1.1.2.1 2013/07/24 02:46:33 riastradh Exp $"); | | 33 | __KERNEL_RCSID(0, "$NetBSD: drm_scatter.c,v 1.1.2.2 2013/07/24 03:27:52 riastradh Exp $"); |
34 | | | 34 | |
35 | #include <sys/types.h> | | 35 | #include <sys/types.h> |
36 | #include <sys/bus.h> | | 36 | #include <sys/bus.h> |
37 | #include <sys/errno.h> | | 37 | #include <sys/errno.h> |
38 | #include <sys/systm.h> | | 38 | #include <sys/systm.h> |
39 | | | 39 | |
40 | #include <linux/slab.h> | | 40 | #include <linux/slab.h> |
41 | | | 41 | |
42 | #include <drm/drmP.h> | | 42 | #include <drm/drmP.h> |
43 | | | 43 | |
44 | static int drm_sg_alloc_mem(struct drm_device *, size_t, | | 44 | static int drm_sg_alloc_mem(struct drm_device *, size_t, |
45 | struct drm_sg_mem **); | | 45 | struct drm_sg_mem **); |
46 | | | 46 | |
47 | int | | 47 | int |
48 | drm_sg_alloc_ioctl(struct drm_device *dev, void *data, | | 48 | drm_sg_alloc_ioctl(struct drm_device *dev, void *data, |
49 | struct drm_file *file __unused) | | 49 | struct drm_file *file __unused) |
50 | { | | 50 | { |
51 | struct drm_scatter_gather *const request = data; | | 51 | struct drm_scatter_gather *const request = data; |
52 | struct drm_sg_mem *sg; | | 52 | struct drm_sg_mem *sg; |
53 | int error; | | 53 | int error; |
54 | | | 54 | |
55 | /* | | 55 | /* |
56 | * XXX Should not hold this mutex during drm_sg_alloc. For | | 56 | * XXX Should not hold this mutex during drm_sg_alloc. For |
57 | * now, we'll just use non-blocking allocations. | | 57 | * now, we'll just use non-blocking allocations. |
58 | */ | | 58 | */ |
59 | KASSERT(mutex_is_locked(&drm_global_mutex)); | | 59 | KASSERT(mutex_is_locked(&drm_global_mutex)); |
60 | | | 60 | |
61 | /* | | 61 | /* |
62 | * Sanity-check the inputs. | | 62 | * Sanity-check the inputs. |
63 | */ | | 63 | */ |
64 | if (!drm_core_check_feature(dev, DRIVER_SG)) | | 64 | if (!drm_core_check_feature(dev, DRIVER_SG)) |
65 | return -ENODEV; | | 65 | return -ENODEV; |
66 | if (dev->sg != NULL) | | 66 | if (dev->sg != NULL) |
67 | return -EBUSY; | | 67 | return -EBUSY; |
68 | if (request->size > 0xffffffffUL) | | 68 | if (request->size > 0xffffffffUL) |
69 | return -ENOMEM; | | 69 | return -ENOMEM; |
70 | | | 70 | |
71 | /* | | 71 | /* |
72 | * Do the allocation. | | 72 | * Do the allocation. |
73 | * | | 73 | * |
74 | * XXX Would it be safe to drop drm_global_mutex here to avoid | | 74 | * XXX Would it be safe to drop drm_global_mutex here to avoid |
75 | * holding it during allocation? | | 75 | * holding it during allocation? |
76 | */ | | 76 | */ |
77 | error = drm_sg_alloc_mem(dev, request->size, &sg); | | 77 | error = drm_sg_alloc_mem(dev, request->size, &sg); |
78 | if (error) | | 78 | if (error) |
79 | return error; | | 79 | return error; |
80 | | | 80 | |
81 | /* Set it up to be the device's scatter-gather memory. */ | | 81 | /* Set it up to be the device's scatter-gather memory. */ |
82 | dev->sg = sg; | | 82 | dev->sg = sg; |
83 | | | 83 | |
84 | /* Success! */ | | 84 | /* Success! */ |
85 | request->handle = sg->handle; | | 85 | request->handle = sg->handle; |
86 | return 0; | | 86 | return 0; |
87 | } | | 87 | } |
88 | | | 88 | |
89 | int | | 89 | int |
90 | drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file) | | 90 | drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file) |
91 | { | | 91 | { |
92 | struct drm_scatter_gather *const request = data; | | 92 | struct drm_scatter_gather *const request = data; |
93 | struct drm_sg_mem *sg; | | 93 | struct drm_sg_mem *sg; |
94 | | | 94 | |
95 | KASSERT(mutex_is_locked(&drm_global_mutex)); | | 95 | KASSERT(mutex_is_locked(&drm_global_mutex)); |
96 | | | 96 | |
97 | /* | | 97 | /* |
98 | * Sanity-check the inputs. | | 98 | * Sanity-check the inputs. |
99 | */ | | 99 | */ |
100 | if (!drm_core_check_feature(dev, DRIVER_SG)) | | 100 | if (!drm_core_check_feature(dev, DRIVER_SG)) |
101 | return -ENODEV; | | 101 | return -ENODEV; |
102 | | | 102 | |
103 | sg = dev->sg; | | 103 | sg = dev->sg; |
104 | if (sg == NULL) | | 104 | if (sg == NULL) |
105 | return -ENXIO; | | 105 | return -ENXIO; |
106 | if (sg->handle != request->handle) | | 106 | if (sg->handle != request->handle) |
107 | return -EINVAL; | | 107 | return -EINVAL; |
108 | | | 108 | |
109 | /* Remove dev->sg. */ | | 109 | /* Remove dev->sg. */ |
110 | dev->sg = NULL; | | 110 | dev->sg = NULL; |
111 | | | 111 | |
112 | /* Success! */ | | 112 | /* Success! */ |
113 | drm_sg_cleanup(sg); | | 113 | drm_sg_cleanup(sg); |
114 | return 0; | | 114 | return 0; |
115 | } | | 115 | } |
116 | | | 116 | |
117 | static int | | 117 | static int |
118 | drm_sg_alloc_mem(struct drm_device *dev, size_t size, struct drm_sg_mem **sgp) | | 118 | drm_sg_alloc_mem(struct drm_device *dev, size_t size, struct drm_sg_mem **sgp) |
119 | { | | 119 | { |
120 | int nsegs; | | 120 | int nsegs; |
121 | int error; | | 121 | int error; |
122 | | | 122 | |
123 | KASSERT(drm_core_check_feature(dev, DRIVER_SG)); | | 123 | KASSERT(drm_core_check_feature(dev, DRIVER_SG)); |
124 | | | 124 | |
125 | KASSERT(size <= (size_t)0xffffffffUL); /* XXX 32-bit sizes only? */ | | 125 | KASSERT(size <= (size_t)0xffffffffUL); /* XXX 32-bit sizes only? */ |
126 | const size_t nbytes = round_page(size); | | 126 | const size_t nbytes = round_page(size); |
127 | const size_t npages = nbytes >> PAGE_SHIFT; | | 127 | const size_t npages = nbytes >> PAGE_SHIFT; |
128 | KASSERT(npages <= (size_t)INT_MAX); | | 128 | KASSERT(npages <= (size_t)INT_MAX); |
129 | | | 129 | |
130 | /* | | 130 | /* |
131 | * Allocate a drm_sg_mem record. | | 131 | * Allocate a drm_sg_mem record. |
132 | */ | | 132 | */ |
133 | struct drm_sg_mem *const sg = | | 133 | struct drm_sg_mem *const sg = |
134 | kzalloc(offsetof(struct drm_sg_mem, sg_segs[npages]), GFP_ATOMIC); | | 134 | kmem_zalloc(offsetof(struct drm_sg_mem, sg_segs[npages]), |
| | | 135 | KM_NOSLEEP); |
135 | if (sg == NULL) | | 136 | if (sg == NULL) |
136 | return -ENOMEM; | | 137 | return -ENOMEM; |
137 | sg->sg_tag = dev->dmat; | | 138 | sg->sg_tag = dev->dmat; |
138 | sg->sg_nsegs_max = (unsigned int)npages; | | 139 | sg->sg_nsegs_max = (unsigned int)npages; |
139 | | | 140 | |
140 | /* | | 141 | /* |
141 | * Allocate the requested amount of DMA-safe memory. | | 142 | * Allocate the requested amount of DMA-safe memory. |
142 | */ | | 143 | */ |
143 | KASSERT(sg->sg_nsegs_max <= (unsigned int)INT_MAX); | | 144 | KASSERT(sg->sg_nsegs_max <= (unsigned int)INT_MAX); |
144 | /* XXX errno NetBSD->Linux */ | | 145 | /* XXX errno NetBSD->Linux */ |
145 | error = -bus_dmamem_alloc(sg->sg_tag, nbytes, PAGE_SIZE, 0, | | 146 | error = -bus_dmamem_alloc(sg->sg_tag, nbytes, PAGE_SIZE, 0, |
146 | sg->sg_segs, (int)sg->sg_nsegs_max, &nsegs, BUS_DMA_NOWAIT); | | 147 | sg->sg_segs, (int)sg->sg_nsegs_max, &nsegs, BUS_DMA_NOWAIT); |
147 | if (error) | | 148 | if (error) |
148 | goto fail0; | | 149 | goto fail0; |
149 | KASSERT(0 <= nsegs); | | 150 | KASSERT(0 <= nsegs); |
150 | sg->sg_nsegs = (unsigned int)nsegs; | | 151 | sg->sg_nsegs = (unsigned int)nsegs; |
151 | | | 152 | |
152 | /* | | 153 | /* |
153 | * XXX Old drm passed DRM_BUS_NOWAIT below but BUS_DMA_WAITOK | | 154 | * XXX Old drm passed DRM_BUS_NOWAIT below but BUS_DMA_WAITOK |
154 | * above. WTF? | | 155 | * above. WTF? |
155 | */ | | 156 | */ |
156 | | | 157 | |
157 | /* | | 158 | /* |
158 | * Map the DMA-safe memory into kernel virtual address space. | | 159 | * Map the DMA-safe memory into kernel virtual address space. |
159 | */ | | 160 | */ |
160 | /* XXX errno NetBSD->Linux */ | | 161 | /* XXX errno NetBSD->Linux */ |
161 | error = -bus_dmamem_map(sg->sg_tag, sg->sg_segs, nsegs, nbytes, | | 162 | error = -bus_dmamem_map(sg->sg_tag, sg->sg_segs, nsegs, nbytes, |
162 | &sg->virtual, | | 163 | &sg->virtual, |
163 | (BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_NOCACHE)); | | 164 | (BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_NOCACHE)); |
164 | if (error) | | 165 | if (error) |
165 | goto fail1; | | 166 | goto fail1; |
166 | sg->sg_size = nbytes; | | 167 | sg->sg_size = nbytes; |
167 | | | 168 | |
168 | /* | | 169 | /* |
169 | * Create a map for DMA transfers. | | 170 | * Create a map for DMA transfers. |
170 | */ | | 171 | */ |
171 | /* XXX errno NetBSD->Linux */ | | 172 | /* XXX errno NetBSD->Linux */ |
172 | error = -bus_dmamap_create(sg->sg_tag, nbytes, nsegs, nbytes, 0, | | 173 | error = -bus_dmamap_create(sg->sg_tag, nbytes, nsegs, nbytes, 0, |
173 | BUS_DMA_NOWAIT, &sg->sg_map); | | 174 | BUS_DMA_NOWAIT, &sg->sg_map); |
174 | if (error) | | 175 | if (error) |
175 | goto fail2; | | 176 | goto fail2; |
176 | | | 177 | |
177 | /* | | 178 | /* |
178 | * Load the kva buffer into the map for DMA transfers. | | 179 | * Load the kva buffer into the map for DMA transfers. |
179 | */ | | 180 | */ |
180 | /* XXX errno NetBSD->Linux */ | | 181 | /* XXX errno NetBSD->Linux */ |
181 | error = -bus_dmamap_load(sg->sg_tag, sg->sg_map, sg->virtual, nbytes, | | 182 | error = -bus_dmamap_load(sg->sg_tag, sg->sg_map, sg->virtual, nbytes, |
182 | NULL, (BUS_DMA_NOWAIT | BUS_DMA_NOCACHE)); | | 183 | NULL, (BUS_DMA_NOWAIT | BUS_DMA_NOCACHE)); |
183 | if (error) | | 184 | if (error) |
184 | goto fail3; | | 185 | goto fail3; |
185 | | | 186 | |
186 | /* | | 187 | /* |
187 | * Use the kernel virtual address as the userland handle. | | 188 | * Use the kernel virtual address as the userland handle. |
188 | * | | 189 | * |
189 | * XXX This leaks some information about kernel virtual | | 190 | * XXX This leaks some information about kernel virtual |
190 | * addresses to userland; should we use an idr instead? | | 191 | * addresses to userland; should we use an idr instead? |
191 | */ | | 192 | */ |
192 | sg->handle = (unsigned long)(uintptr_t)sg->virtual; | | 193 | sg->handle = (unsigned long)(uintptr_t)sg->virtual; |
193 | KASSERT(sg->virtual == (void *)(uintptr_t)sg->handle); | | 194 | KASSERT(sg->virtual == (void *)(uintptr_t)sg->handle); |
194 | | | 195 | |
195 | /* Success! */ | | 196 | /* Success! */ |
196 | *sgp = sg; | | 197 | *sgp = sg; |
197 | return 0; | | 198 | return 0; |
198 | | | 199 | |
199 | fail3: bus_dmamap_destroy(sg->sg_tag, sg->sg_map); | | 200 | fail3: bus_dmamap_destroy(sg->sg_tag, sg->sg_map); |
200 | fail2: bus_dmamem_unmap(sg->sg_tag, sg->virtual, sg->sg_size); | | 201 | fail2: bus_dmamem_unmap(sg->sg_tag, sg->virtual, sg->sg_size); |
201 | fail1: KASSERT(sg->sg_nsegs <= (unsigned int)INT_MAX); | | 202 | fail1: KASSERT(sg->sg_nsegs <= (unsigned int)INT_MAX); |
202 | bus_dmamem_free(sg->sg_tag, sg->sg_segs, (int)sg->sg_nsegs); | | 203 | bus_dmamem_free(sg->sg_tag, sg->sg_segs, (int)sg->sg_nsegs); |
203 | fail0: sg->sg_tag = NULL; /* XXX paranoia */ | | 204 | fail0: sg->sg_tag = NULL; /* XXX paranoia */ |
204 | kfree(sg); | | 205 | kfree(sg); |
205 | return error; | | 206 | return error; |
206 | } | | 207 | } |
207 | | | 208 | |
208 | void | | 209 | void |
209 | drm_sg_cleanup(struct drm_sg_mem *sg) | | 210 | drm_sg_cleanup(struct drm_sg_mem *sg) |
210 | { | | 211 | { |
211 | | | 212 | |
212 | bus_dmamap_unload(sg->sg_tag, sg->sg_map); | | 213 | bus_dmamap_unload(sg->sg_tag, sg->sg_map); |
213 | bus_dmamap_destroy(sg->sg_tag, sg->sg_map); | | 214 | bus_dmamap_destroy(sg->sg_tag, sg->sg_map); |
214 | bus_dmamem_unmap(sg->sg_tag, sg->virtual, sg->sg_size); | | 215 | bus_dmamem_unmap(sg->sg_tag, sg->virtual, sg->sg_size); |
215 | KASSERT(sg->sg_nsegs <= (unsigned int)INT_MAX); | | 216 | KASSERT(sg->sg_nsegs <= (unsigned int)INT_MAX); |
216 | bus_dmamem_free(sg->sg_tag, sg->sg_segs, (int)sg->sg_nsegs); | | 217 | bus_dmamem_free(sg->sg_tag, sg->sg_segs, (int)sg->sg_nsegs); |
217 | sg->sg_tag = NULL; /* XXX paranoia */ | | 218 | sg->sg_tag = NULL; /* XXX paranoia */ |
218 | kfree(sg); | | 219 | kmem_free(sg, offsetof(struct drm_sg_mem, sg_segs[sg->sg_nsegs_max])); |
219 | } | | 220 | } |