1 /************************************************************************** 2 * 3 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Tx., USA 4 * All Rights Reserved. 5 * Copyright 2009 VMware, Inc., Palo Alto, CA., USA 6 * All Rights Reserved. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sub license, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice (including the 17 * next paragraph) shall be included in all copies or substantial portions 18 * of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 23 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 24 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 25 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 26 * USE OR OTHER DEALINGS IN THE SOFTWARE. 27 * 28 **************************************************************************/ 29 /* 30 * Authors: Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> 31 */ 32 33 #ifdef HAVE_CONFIG_H 34 #include "config.h" 35 #endif 36 37 #include "wsbm_fencemgr.h" 38 #include "wsbm_pool.h" 39 #include "wsbm_manager.h" 40 #include <xf86drm.h> 41 #include <drm/psb_ttm_fence_user.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 struct _WsbmFenceClass 46 { 47 struct _WsbmListHead head; 48 struct _WsbmMutex mutex; 49 struct _WsbmMutex cmd_mutex; 50 }; 51 52 /* 53 * Note: The struct _WsbmFenceMgr::Mutex should never be held 54 * during sleeps, since that may block fast concurrent access to 55 * fence data. 56 */ 57 58 struct _WsbmFenceMgr 59 { 60 /* 61 * Constant members. Need no mutex protection. 62 */ 63 struct _WsbmFenceMgrCreateInfo info; 64 void *private; 65 66 /* 67 * Atomic members. No mutex protection. 68 */ 69 70 struct _WsbmAtomic count; 71 72 /* 73 * These members are protected by this->mutex 74 */ 75 76 struct _WsbmFenceClass *classes; 77 uint32_t num_classes; 78 }; 79 80 struct _WsbmFenceObject 81 { 82 83 /* 84 * These members are constant and need no mutex protection. 85 * Note that @private may point to a structure with its own 86 * mutex protection, that we don't care about. 87 */ 88 89 struct _WsbmFenceMgr *mgr; 90 uint32_t fence_class; 91 uint32_t fence_type; 92 void *private; 93 94 /* 95 * Atomic members. No mutex protection. note that 96 * @signaled types is updated using a compare-and-swap 97 * scheme to guarantee atomicity. 98 */ 99 100 struct _WsbmAtomic refCount; 101 struct _WsbmAtomic signaled_types; 102 103 /* 104 * These members are protected by mgr->mutex. 105 */ 106 struct _WsbmListHead head; 107 }; 108 109 uint32_t 110 wsbmFenceType(struct _WsbmFenceObject *fence) 111 { 112 return fence->fence_type; 113 } 114 115 struct _WsbmFenceMgr * 116 wsbmFenceMgrCreate(const struct _WsbmFenceMgrCreateInfo *info) 117 { 118 struct _WsbmFenceMgr *tmp; 119 uint32_t i, j; 120 int ret; 121 122 tmp = calloc(1, sizeof(*tmp)); 123 if (!tmp) 124 return NULL; 125 126 tmp->info = *info; 127 tmp->classes = calloc(tmp->info.num_classes, sizeof(*tmp->classes)); 128 if (!tmp->classes) 129 goto out_err; 130 131 for (i = 0; i < tmp->info.num_classes; ++i) { 132 struct _WsbmFenceClass *fc = &tmp->classes[i]; 133 134 WSBMINITLISTHEAD(&fc->head); 135 ret = WSBM_MUTEX_INIT(&fc->mutex); 136 if (ret) 137 goto out_err1; 138 ret = WSBM_MUTEX_INIT(&fc->cmd_mutex); 139 if (ret) { 140 WSBM_MUTEX_FREE(&fc->mutex); 141 goto out_err1; 142 } 143 } 144 wsbmAtomicSet(&tmp->count, 0); 145 146 return tmp; 147 148 out_err1: 149 for (j = 0; j < i; ++j) { 150 WSBM_MUTEX_FREE(&tmp->classes[j].mutex); 151 WSBM_MUTEX_FREE(&tmp->classes[j].cmd_mutex); 152 } 153 free(tmp->classes); 154 out_err: 155 if (tmp) 156 free(tmp); 157 return NULL; 158 } 159 160 void 161 wsbmFenceUnreference(struct _WsbmFenceObject **pFence) 162 { 163 struct _WsbmFenceObject *fence = *pFence; 164 struct _WsbmFenceMgr *mgr; 165 166 *pFence = NULL; 167 if (fence == NULL) 168 return; 169 170 mgr = fence->mgr; 171 if (wsbmAtomicDecZero(&fence->refCount)) { 172 struct _WsbmFenceClass *fc = &mgr->classes[fence->fence_class]; 173 174 WSBM_MUTEX_LOCK(&fc->mutex); 175 WSBMLISTDELINIT(&fence->head); 176 WSBM_MUTEX_UNLOCK(&fc->mutex); 177 if (fence->private) 178 mgr->info.unreference(mgr, &fence->private); 179 fence->mgr = NULL; 180 wsbmAtomicDecZero(&mgr->count); 181 free(fence); 182 } 183 } 184 185 static void 186 wsbmSignalPreviousFences(struct _WsbmFenceMgr *mgr, 187 struct _WsbmListHead *list, 188 uint32_t fence_class, uint32_t signaled_types) 189 { 190 struct _WsbmFenceClass *fc = &mgr->classes[fence_class]; 191 struct _WsbmFenceObject *entry; 192 struct _WsbmListHead *prev; 193 uint32_t old_signaled_types; 194 uint32_t ret_st; 195 196 WSBM_MUTEX_LOCK(&fc->mutex); 197 while (list != &fc->head && list->next != list) { 198 entry = WSBMLISTENTRY(list, struct _WsbmFenceObject, head); 199 200 prev = list->prev; 201 202 do { 203 old_signaled_types = wsbmAtomicRead(&entry->signaled_types); 204 signaled_types = 205 old_signaled_types | (signaled_types & entry->fence_type); 206 if (signaled_types == old_signaled_types) 207 break; 208 209 ret_st = 210 wsbmAtomicCmpXchg(&entry->signaled_types, old_signaled_types, 211 signaled_types); 212 } while (ret_st != old_signaled_types); 213 214 if (signaled_types == entry->fence_type) 215 WSBMLISTDELINIT(list); 216 217 list = prev; 218 } 219 WSBM_MUTEX_UNLOCK(&fc->mutex); 220 } 221 222 int 223 wsbmFenceFinish(struct _WsbmFenceObject *fence, uint32_t fence_type, 224 int lazy_hint) 225 { 226 struct _WsbmFenceMgr *mgr = fence->mgr; 227 int ret = 0; 228 229 if ((wsbmAtomicRead(&fence->signaled_types) & fence_type) == fence_type) 230 goto out; 231 232 ret = mgr->info.finish(mgr, fence->private, fence_type, lazy_hint); 233 if (ret) 234 goto out; 235 236 wsbmSignalPreviousFences(mgr, &fence->head, fence->fence_class, 237 fence_type); 238 out: 239 return ret; 240 } 241 242 uint32_t 243 wsbmFenceSignaledTypeCached(struct _WsbmFenceObject * fence) 244 { 245 return wsbmAtomicRead(&fence->signaled_types); 246 } 247 248 int 249 wsbmFenceSignaledType(struct _WsbmFenceObject *fence, uint32_t flush_type, 250 uint32_t * signaled) 251 { 252 int ret = 0; 253 struct _WsbmFenceMgr *mgr; 254 uint32_t signaled_types; 255 uint32_t old_signaled_types; 256 uint32_t ret_st; 257 258 mgr = fence->mgr; 259 *signaled = wsbmAtomicRead(&fence->signaled_types); 260 if ((*signaled & flush_type) == flush_type) 261 goto out0; 262 263 ret = mgr->info.signaled(mgr, fence->private, flush_type, signaled); 264 if (ret) { 265 *signaled = wsbmAtomicRead(&fence->signaled_types); 266 goto out0; 267 } 268 269 do { 270 old_signaled_types = wsbmAtomicRead(&fence->signaled_types); 271 signaled_types = old_signaled_types | *signaled; 272 if (signaled_types == old_signaled_types) 273 break; 274 275 ret_st = wsbmAtomicCmpXchg(&fence->signaled_types, old_signaled_types, 276 signaled_types); 277 if (old_signaled_types == ret_st) 278 wsbmSignalPreviousFences(mgr, &fence->head, fence->fence_class, 279 *signaled); 280 } while (old_signaled_types != ret_st); 281 282 return 0; 283 out0: 284 return ret; 285 } 286 287 struct _WsbmFenceObject * 288 wsbmFenceReference(struct _WsbmFenceObject *fence) 289 { 290 if (fence == NULL) 291 return NULL; 292 wsbmAtomicInc(&fence->refCount); 293 return fence; 294 } 295 296 struct _WsbmFenceObject * 297 wsbmFenceCreateSig(struct _WsbmFenceMgr *mgr, uint32_t fence_class, 298 uint32_t fence_type, uint32_t signaled_types, 299 void *private, size_t private_size) 300 { 301 struct _WsbmFenceClass *fc = &mgr->classes[fence_class]; 302 struct _WsbmFenceObject *fence; 303 size_t fence_size = sizeof(*fence); 304 305 if (private_size) 306 fence_size = ((fence_size + 15) & ~15); 307 308 fence = calloc(1, fence_size + private_size); 309 310 if (!fence) 311 goto out_err; 312 313 wsbmAtomicSet(&fence->refCount, 1); 314 fence->mgr = mgr; 315 fence->fence_class = fence_class; 316 fence->fence_type = fence_type; 317 wsbmAtomicSet(&fence->signaled_types, signaled_types); 318 fence->private = private; 319 if (private_size) { 320 fence->private = (void *)(((uint8_t *) fence) + fence_size); 321 memcpy(fence->private, private, private_size); 322 } 323 324 WSBM_MUTEX_LOCK(&fc->mutex); 325 WSBMLISTADDTAIL(&fence->head, &fc->head); 326 WSBM_MUTEX_UNLOCK(&fc->mutex); 327 wsbmAtomicInc(&mgr->count); 328 return fence; 329 330 out_err: 331 { 332 int ret = mgr->info.finish(mgr, private, fence_type, 0); 333 334 if (ret) 335 usleep(10000000); 336 } 337 if (fence) 338 free(fence); 339 340 mgr->info.unreference(mgr, &private); 341 return NULL; 342 } 343 344 struct _WsbmFenceObject * 345 wsbmFenceCreate(struct _WsbmFenceMgr *mgr, uint32_t fence_class, 346 uint32_t fence_type, void *private, size_t private_size) 347 { 348 return wsbmFenceCreateSig(mgr, fence_class, fence_type, 0, private, 349 private_size); 350 } 351 352 struct _WsbmTTMFenceMgrPriv 353 { 354 int fd; 355 unsigned int devOffset; 356 }; 357 358 static int 359 tSignaled(struct _WsbmFenceMgr *mgr, void *private, uint32_t flush_type, 360 uint32_t * signaled_type) 361 { 362 struct _WsbmTTMFenceMgrPriv *priv = 363 (struct _WsbmTTMFenceMgrPriv *)mgr->private; 364 union ttm_fence_signaled_arg arg; 365 int ret; 366 367 arg.req.handle = (unsigned long)private; 368 arg.req.fence_type = flush_type; 369 arg.req.flush = 1; 370 *signaled_type = 0; 371 372 ret = drmCommandWriteRead(priv->fd, priv->devOffset + TTM_FENCE_SIGNALED, 373 &arg, sizeof(arg)); 374 if (ret) 375 return ret; 376 377 *signaled_type = arg.rep.signaled_types; 378 return 0; 379 } 380 381 static int 382 tFinish(struct _WsbmFenceMgr *mgr, void *private, uint32_t fence_type, 383 int lazy_hint) 384 { 385 struct _WsbmTTMFenceMgrPriv *priv = 386 (struct _WsbmTTMFenceMgrPriv *)mgr->private; 387 union ttm_fence_finish_arg arg = 388 {.req = {.handle = (unsigned long)private, 389 .fence_type = fence_type, 390 .mode = (lazy_hint) ? TTM_FENCE_FINISH_MODE_LAZY : 0} 391 }; 392 int ret; 393 394 do { 395 ret = drmCommandWriteRead(priv->fd, priv->devOffset + TTM_FENCE_FINISH, 396 &arg, sizeof(arg)); 397 } while (ret == -EAGAIN || ret == -ERESTART); 398 399 return ret; 400 } 401 402 static int 403 tUnref(struct _WsbmFenceMgr *mgr, void **private) 404 { 405 struct _WsbmTTMFenceMgrPriv *priv = 406 (struct _WsbmTTMFenceMgrPriv *)mgr->private; 407 struct ttm_fence_unref_arg arg = {.handle = (unsigned long)*private }; 408 409 *private = NULL; 410 411 return drmCommandWrite(priv->fd, priv->devOffset + TTM_FENCE_UNREF, 412 &arg, sizeof(arg)); 413 } 414 415 struct _WsbmFenceMgr * 416 wsbmFenceMgrTTMInit(int fd, unsigned int numClass, unsigned int devOffset) 417 { 418 struct _WsbmFenceMgrCreateInfo info; 419 struct _WsbmFenceMgr *mgr; 420 struct _WsbmTTMFenceMgrPriv *priv = malloc(sizeof(*priv)); 421 422 if (!priv) 423 return NULL; 424 425 priv->fd = fd; 426 priv->devOffset = devOffset; 427 428 info.flags = WSBM_FENCE_CLASS_ORDERED; 429 info.num_classes = numClass; 430 info.signaled = tSignaled; 431 info.finish = tFinish; 432 info.unreference = tUnref; 433 434 mgr = wsbmFenceMgrCreate(&info); 435 if (mgr == NULL) { 436 free(priv); 437 return NULL; 438 } 439 440 mgr->private = (void *)priv; 441 return mgr; 442 } 443 444 void 445 wsbmFenceCmdLock(struct _WsbmFenceMgr *mgr, uint32_t fence_class) 446 { 447 WSBM_MUTEX_LOCK(&mgr->classes[fence_class].cmd_mutex); 448 } 449 450 void 451 wsbmFenceCmdUnlock(struct _WsbmFenceMgr *mgr, uint32_t fence_class) 452 { 453 WSBM_MUTEX_UNLOCK(&mgr->classes[fence_class].cmd_mutex); 454 } 455 456 void 457 wsbmFenceMgrTTMTakedown(struct _WsbmFenceMgr *mgr) 458 { 459 unsigned int i; 460 461 if (!mgr) 462 return; 463 464 if (mgr->private) 465 free(mgr->private); 466 467 for (i = 0; i < mgr->info.num_classes; ++i) { 468 WSBM_MUTEX_FREE(&mgr->classes[i].mutex); 469 WSBM_MUTEX_FREE(&mgr->classes[i].cmd_mutex); 470 } 471 free(mgr); 472 473 return; 474 } 475