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 } 275