Commit | Line | Data |
---|---|---|
543831cf TH |
1 | /************************************************************************** |
2 | * | |
3 | * Copyright © 2009-2012 VMware, Inc., Palo Alto, CA., USA | |
4 | * All Rights Reserved. | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a | |
7 | * copy of this software and associated documentation files (the | |
8 | * "Software"), to deal in the Software without restriction, including | |
9 | * without limitation the rights to use, copy, modify, merge, publish, | |
10 | * distribute, sub license, and/or sell copies of the Software, and to | |
11 | * permit persons to whom the Software is furnished to do so, subject to | |
12 | * the following conditions: | |
13 | * | |
14 | * The above copyright notice and this permission notice (including the | |
15 | * next paragraph) shall be included in all copies or substantial portions | |
16 | * of the Software. | |
17 | * | |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | |
21 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | |
22 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
23 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
24 | * USE OR OTHER DEALINGS IN THE SOFTWARE. | |
25 | * | |
26 | **************************************************************************/ | |
27 | ||
28 | #include "vmwgfx_drv.h" | |
29 | #include "vmwgfx_resource_priv.h" | |
30 | #include "ttm/ttm_placement.h" | |
31 | ||
32 | struct vmw_user_context { | |
33 | struct ttm_base_object base; | |
34 | struct vmw_resource res; | |
35 | }; | |
36 | ||
37 | static void vmw_user_context_free(struct vmw_resource *res); | |
38 | static struct vmw_resource * | |
39 | vmw_user_context_base_to_res(struct ttm_base_object *base); | |
40 | ||
41 | static uint64_t vmw_user_context_size; | |
42 | ||
43 | static const struct vmw_user_resource_conv user_context_conv = { | |
44 | .object_type = VMW_RES_CONTEXT, | |
45 | .base_obj_to_res = vmw_user_context_base_to_res, | |
46 | .res_free = vmw_user_context_free | |
47 | }; | |
48 | ||
49 | const struct vmw_user_resource_conv *user_context_converter = | |
50 | &user_context_conv; | |
51 | ||
52 | ||
53 | static const struct vmw_res_func vmw_legacy_context_func = { | |
54 | .res_type = vmw_res_context, | |
55 | .needs_backup = false, | |
56 | .may_evict = false, | |
57 | .type_name = "legacy contexts", | |
58 | .backup_placement = NULL, | |
59 | .create = NULL, | |
60 | .destroy = NULL, | |
61 | .bind = NULL, | |
62 | .unbind = NULL | |
63 | }; | |
64 | ||
65 | /** | |
66 | * Context management: | |
67 | */ | |
68 | ||
69 | static void vmw_hw_context_destroy(struct vmw_resource *res) | |
70 | { | |
71 | ||
72 | struct vmw_private *dev_priv = res->dev_priv; | |
73 | struct { | |
74 | SVGA3dCmdHeader header; | |
75 | SVGA3dCmdDestroyContext body; | |
76 | } *cmd; | |
77 | ||
78 | ||
79 | vmw_execbuf_release_pinned_bo(dev_priv); | |
80 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | |
81 | if (unlikely(cmd == NULL)) { | |
82 | DRM_ERROR("Failed reserving FIFO space for surface " | |
83 | "destruction.\n"); | |
84 | return; | |
85 | } | |
86 | ||
87 | cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DESTROY); | |
88 | cmd->header.size = cpu_to_le32(sizeof(cmd->body)); | |
89 | cmd->body.cid = cpu_to_le32(res->id); | |
90 | ||
91 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | |
92 | vmw_3d_resource_dec(dev_priv, false); | |
93 | } | |
94 | ||
95 | static int vmw_context_init(struct vmw_private *dev_priv, | |
96 | struct vmw_resource *res, | |
97 | void (*res_free) (struct vmw_resource *res)) | |
98 | { | |
99 | int ret; | |
100 | ||
101 | struct { | |
102 | SVGA3dCmdHeader header; | |
103 | SVGA3dCmdDefineContext body; | |
104 | } *cmd; | |
105 | ||
106 | ret = vmw_resource_init(dev_priv, res, false, | |
107 | res_free, &vmw_legacy_context_func); | |
108 | ||
109 | if (unlikely(ret != 0)) { | |
110 | DRM_ERROR("Failed to allocate a resource id.\n"); | |
111 | goto out_early; | |
112 | } | |
113 | ||
114 | if (unlikely(res->id >= SVGA3D_MAX_CONTEXT_IDS)) { | |
115 | DRM_ERROR("Out of hw context ids.\n"); | |
116 | vmw_resource_unreference(&res); | |
117 | return -ENOMEM; | |
118 | } | |
119 | ||
120 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | |
121 | if (unlikely(cmd == NULL)) { | |
122 | DRM_ERROR("Fifo reserve failed.\n"); | |
123 | vmw_resource_unreference(&res); | |
124 | return -ENOMEM; | |
125 | } | |
126 | ||
127 | cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DEFINE); | |
128 | cmd->header.size = cpu_to_le32(sizeof(cmd->body)); | |
129 | cmd->body.cid = cpu_to_le32(res->id); | |
130 | ||
131 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | |
132 | (void) vmw_3d_resource_inc(dev_priv, false); | |
133 | vmw_resource_activate(res, vmw_hw_context_destroy); | |
134 | return 0; | |
135 | ||
136 | out_early: | |
137 | if (res_free == NULL) | |
138 | kfree(res); | |
139 | else | |
140 | res_free(res); | |
141 | return ret; | |
142 | } | |
143 | ||
144 | struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv) | |
145 | { | |
146 | struct vmw_resource *res = kmalloc(sizeof(*res), GFP_KERNEL); | |
147 | int ret; | |
148 | ||
149 | if (unlikely(res == NULL)) | |
150 | return NULL; | |
151 | ||
152 | ret = vmw_context_init(dev_priv, res, NULL); | |
153 | ||
154 | return (ret == 0) ? res : NULL; | |
155 | } | |
156 | ||
157 | /** | |
158 | * User-space context management: | |
159 | */ | |
160 | ||
161 | static struct vmw_resource * | |
162 | vmw_user_context_base_to_res(struct ttm_base_object *base) | |
163 | { | |
164 | return &(container_of(base, struct vmw_user_context, base)->res); | |
165 | } | |
166 | ||
167 | static void vmw_user_context_free(struct vmw_resource *res) | |
168 | { | |
169 | struct vmw_user_context *ctx = | |
170 | container_of(res, struct vmw_user_context, res); | |
171 | struct vmw_private *dev_priv = res->dev_priv; | |
172 | ||
173 | ttm_base_object_kfree(ctx, base); | |
174 | ttm_mem_global_free(vmw_mem_glob(dev_priv), | |
175 | vmw_user_context_size); | |
176 | } | |
177 | ||
178 | /** | |
179 | * This function is called when user space has no more references on the | |
180 | * base object. It releases the base-object's reference on the resource object. | |
181 | */ | |
182 | ||
183 | static void vmw_user_context_base_release(struct ttm_base_object **p_base) | |
184 | { | |
185 | struct ttm_base_object *base = *p_base; | |
186 | struct vmw_user_context *ctx = | |
187 | container_of(base, struct vmw_user_context, base); | |
188 | struct vmw_resource *res = &ctx->res; | |
189 | ||
190 | *p_base = NULL; | |
191 | vmw_resource_unreference(&res); | |
192 | } | |
193 | ||
194 | int vmw_context_destroy_ioctl(struct drm_device *dev, void *data, | |
195 | struct drm_file *file_priv) | |
196 | { | |
197 | struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; | |
198 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | |
199 | ||
200 | return ttm_ref_object_base_unref(tfile, arg->cid, TTM_REF_USAGE); | |
201 | } | |
202 | ||
203 | int vmw_context_define_ioctl(struct drm_device *dev, void *data, | |
204 | struct drm_file *file_priv) | |
205 | { | |
206 | struct vmw_private *dev_priv = vmw_priv(dev); | |
207 | struct vmw_user_context *ctx; | |
208 | struct vmw_resource *res; | |
209 | struct vmw_resource *tmp; | |
210 | struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; | |
211 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | |
212 | struct vmw_master *vmaster = vmw_master(file_priv->master); | |
213 | int ret; | |
214 | ||
215 | ||
216 | /* | |
217 | * Approximate idr memory usage with 128 bytes. It will be limited | |
218 | * by maximum number_of contexts anyway. | |
219 | */ | |
220 | ||
221 | if (unlikely(vmw_user_context_size == 0)) | |
222 | vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128; | |
223 | ||
224 | ret = ttm_read_lock(&vmaster->lock, true); | |
225 | if (unlikely(ret != 0)) | |
226 | return ret; | |
227 | ||
228 | ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), | |
229 | vmw_user_context_size, | |
230 | false, true); | |
231 | if (unlikely(ret != 0)) { | |
232 | if (ret != -ERESTARTSYS) | |
233 | DRM_ERROR("Out of graphics memory for context" | |
234 | " creation.\n"); | |
235 | goto out_unlock; | |
236 | } | |
237 | ||
238 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | |
239 | if (unlikely(ctx == NULL)) { | |
240 | ttm_mem_global_free(vmw_mem_glob(dev_priv), | |
241 | vmw_user_context_size); | |
242 | ret = -ENOMEM; | |
243 | goto out_unlock; | |
244 | } | |
245 | ||
246 | res = &ctx->res; | |
247 | ctx->base.shareable = false; | |
248 | ctx->base.tfile = NULL; | |
249 | ||
250 | /* | |
251 | * From here on, the destructor takes over resource freeing. | |
252 | */ | |
253 | ||
254 | ret = vmw_context_init(dev_priv, res, vmw_user_context_free); | |
255 | if (unlikely(ret != 0)) | |
256 | goto out_unlock; | |
257 | ||
258 | tmp = vmw_resource_reference(&ctx->res); | |
259 | ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT, | |
260 | &vmw_user_context_base_release, NULL); | |
261 | ||
262 | if (unlikely(ret != 0)) { | |
263 | vmw_resource_unreference(&tmp); | |
264 | goto out_err; | |
265 | } | |
266 | ||
267 | arg->cid = ctx->base.hash.key; | |
268 | out_err: | |
269 | vmw_resource_unreference(&res); | |
270 | out_unlock: | |
271 | ttm_read_unlock(&vmaster->lock); | |
272 | return ret; | |
273 | ||
274 | } |