wsbm_manager.c revision ac747113d4f6739b1462ca7fb40f2091691e209b
1/**************************************************************************
2 *
3 * Copyright 2006-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 Hellstr�m <thomas-at-tungstengraphics-dot-com>
31 *          Keith Whitwell <keithw-at-tungstengraphics-dot-com>
32 */
33
34#ifdef HAVE_CONFIG_H
35#include "config.h"
36#endif
37
38#include <stdlib.h>
39#include "errno.h"
40#include "string.h"
41#include "wsbm_pool.h"
42#include "wsbm_manager.h"
43#include "wsbm_fencemgr.h"
44#include "wsbm_driver.h"
45#include "wsbm_priv.h"
46#include "wsbm_util.h"
47#include "wsbm_atomic.h"
48#include "assert.h"
49
50#define WSBM_BODATA_SIZE_ACCEPT 4096
51
52#define WSBM_BUFFER_COMPLEX 0
53#define WSBM_BUFFER_SIMPLE  1
54#define WSBM_BUFFER_REF     2
55
56struct _ValidateList
57{
58    unsigned numTarget;
59    unsigned numCurrent;
60    unsigned numOnList;
61    unsigned hashSize;
62    uint32_t hashMask;
63    int driverData;
64    struct _WsbmListHead list;
65    struct _WsbmListHead free;
66    struct _WsbmListHead *hashTable;
67};
68
69struct _WsbmBufferObject
70{
71    /* Left to the client to protect this data for now. */
72
73    struct _WsbmAtomic refCount;
74    struct _WsbmBufStorage *storage;
75
76    uint32_t placement;
77    unsigned alignment;
78    unsigned bufferType;
79    struct _WsbmBufferPool *pool;
80};
81
82struct _WsbmBufferList
83{
84    int hasKernelBuffers;
85
86    struct _ValidateList kernelBuffers;	/* List of kernel buffers needing validation */
87    struct _ValidateList userBuffers;  /* List of user-space buffers needing validation */
88};
89
90static struct _WsbmMutex bmMutex;
91static struct _WsbmCond bmCond;
92static int initialized = 0;
93static void *commonData = NULL;
94
95static int kernelReaders = 0;
96static int kernelLocked = 0;
97
98int
99wsbmInit(struct _WsbmThreadFuncs *tf, struct _WsbmVNodeFuncs *vf)
100{
101    int ret;
102
103    wsbmCurThreadFunc = tf;
104    wsbmCurVNodeFunc = vf;
105
106    ret = WSBM_MUTEX_INIT(&bmMutex);
107    if (ret)
108	return -ENOMEM;
109    ret = WSBM_COND_INIT(&bmCond);
110    if (ret) {
111	WSBM_MUTEX_FREE(&bmMutex);
112	return -ENOMEM;
113    }
114
115    initialized = 1;
116    return 0;
117}
118
119void
120wsbmCommonDataSet(void *d)
121{
122    commonData = d;
123}
124
125void *
126wsbmCommonDataGet(void)
127{
128    return commonData;
129}
130
131int
132wsbmIsInitialized(void)
133{
134    return initialized;
135}
136
137void
138wsbmTakedown(void)
139{
140    initialized = 0;
141    commonData = NULL;
142    WSBM_COND_FREE(&bmCond);
143    WSBM_MUTEX_FREE(&bmMutex);
144}
145
146static struct _ValidateNode *
147validateListAddNode(struct _ValidateList *list, void *item,
148		    uint32_t hash, uint64_t flags, uint64_t mask)
149{
150    struct _ValidateNode *node;
151    struct _WsbmListHead *l;
152    struct _WsbmListHead *hashHead;
153
154    l = list->free.next;
155    if (l == &list->free) {
156	node = wsbmVNodeFuncs()->alloc(wsbmVNodeFuncs(), 0);
157	if (!node) {
158	    return NULL;
159	}
160	list->numCurrent++;
161    } else {
162	WSBMLISTDEL(l);
163	node = WSBMLISTENTRY(l, struct _ValidateNode, head);
164    }
165    node->buf = item;
166    node->set_flags = flags & mask;
167    node->clr_flags = (~flags) & mask;
168    node->listItem = list->numOnList;
169    WSBMLISTADDTAIL(&node->head, &list->list);
170    list->numOnList++;
171    hashHead = list->hashTable + hash;
172    WSBMLISTADDTAIL(&node->hashHead, hashHead);
173
174    return node;
175}
176
177static uint32_t
178wsbmHashFunc(uint8_t * key, uint32_t len, uint32_t mask)
179{
180    uint32_t hash, i;
181
182    for (hash = 0, i = 0; i < len; ++i) {
183	hash += *key++;
184	hash += (hash << 10);
185	hash ^= (hash >> 6);
186    }
187
188    hash += (hash << 3);
189    hash ^= (hash >> 11);
190    hash += (hash << 15);
191
192    return hash & mask;
193}
194
195static void
196validateFreeList(struct _ValidateList *list)
197{
198    struct _ValidateNode *node;
199    struct _WsbmListHead *l;
200
201    l = list->list.next;
202    while (l != &list->list) {
203	WSBMLISTDEL(l);
204	node = WSBMLISTENTRY(l, struct _ValidateNode, head);
205
206	WSBMLISTDEL(&node->hashHead);
207	node->func->free(node);
208	l = list->list.next;
209	list->numCurrent--;
210	list->numOnList--;
211    }
212
213    l = list->free.next;
214    while (l != &list->free) {
215	WSBMLISTDEL(l);
216	node = WSBMLISTENTRY(l, struct _ValidateNode, head);
217
218	node->func->free(node);
219	l = list->free.next;
220	list->numCurrent--;
221    }
222    free(list->hashTable);
223}
224
225static int
226validateListAdjustNodes(struct _ValidateList *list)
227{
228    struct _ValidateNode *node;
229    struct _WsbmListHead *l;
230    int ret = 0;
231
232    while (list->numCurrent < list->numTarget) {
233	node = wsbmVNodeFuncs()->alloc(wsbmVNodeFuncs(), list->driverData);
234	if (!node) {
235	    ret = -ENOMEM;
236	    break;
237	}
238	list->numCurrent++;
239	WSBMLISTADD(&node->head, &list->free);
240    }
241
242    while (list->numCurrent > list->numTarget) {
243	l = list->free.next;
244	if (l == &list->free)
245	    break;
246	WSBMLISTDEL(l);
247	node = WSBMLISTENTRY(l, struct _ValidateNode, head);
248
249	node->func->free(node);
250	list->numCurrent--;
251    }
252    return ret;
253}
254
255static inline int
256wsbmPot(unsigned int val)
257{
258    unsigned int shift = 0;
259    while(val > (unsigned int)(1 << shift))
260	shift++;
261
262    return shift;
263}
264
265
266
267static int
268validateCreateList(int numTarget, struct _ValidateList *list, int driverData)
269{
270    unsigned int i;
271    unsigned int shift = wsbmPot(numTarget);
272    int ret;
273
274    list->hashSize = (1 << shift);
275    list->hashMask = list->hashSize - 1;
276
277    list->hashTable = malloc(list->hashSize * sizeof(*list->hashTable));
278    if (!list->hashTable)
279	return -ENOMEM;
280
281    for (i = 0; i < list->hashSize; ++i)
282	WSBMINITLISTHEAD(&list->hashTable[i]);
283
284    WSBMINITLISTHEAD(&list->list);
285    WSBMINITLISTHEAD(&list->free);
286    list->numTarget = numTarget;
287    list->numCurrent = 0;
288    list->numOnList = 0;
289    list->driverData = driverData;
290    ret = validateListAdjustNodes(list);
291    if (ret != 0)
292	free(list->hashTable);
293
294    return ret;
295}
296
297static int
298validateResetList(struct _ValidateList *list)
299{
300    struct _WsbmListHead *l;
301    struct _ValidateNode *node;
302    int ret;
303
304    ret = validateListAdjustNodes(list);
305    if (ret)
306	return ret;
307
308    l = list->list.next;
309    while (l != &list->list) {
310	WSBMLISTDEL(l);
311	node = WSBMLISTENTRY(l, struct _ValidateNode, head);
312
313	WSBMLISTDEL(&node->hashHead);
314	WSBMLISTADD(l, &list->free);
315	list->numOnList--;
316	l = list->list.next;
317    }
318    return validateListAdjustNodes(list);
319}
320
321void
322wsbmWriteLockKernelBO(void)
323{
324    WSBM_MUTEX_LOCK(&bmMutex);
325    while (kernelReaders != 0)
326	WSBM_COND_WAIT(&bmCond, &bmMutex);
327    kernelLocked = 1;
328}
329
330void
331wsbmWriteUnlockKernelBO(void)
332{
333    kernelLocked = 0;
334    WSBM_MUTEX_UNLOCK(&bmMutex);
335}
336
337void
338wsbmReadLockKernelBO(void)
339{
340    WSBM_MUTEX_LOCK(&bmMutex);
341    if (kernelReaders++ == 0)
342	kernelLocked = 1;
343    WSBM_MUTEX_UNLOCK(&bmMutex);
344}
345
346void
347wsbmReadUnlockKernelBO(void)
348{
349    WSBM_MUTEX_LOCK(&bmMutex);
350    if (--kernelReaders == 0) {
351	kernelLocked = 0;
352	WSBM_COND_BROADCAST(&bmCond);
353    }
354    WSBM_MUTEX_UNLOCK(&bmMutex);
355}
356
357void
358wsbmBOWaitIdle(struct _WsbmBufferObject *buf, int lazy)
359{
360    struct _WsbmBufStorage *storage;
361
362    storage = buf->storage;
363    if (!storage)
364	return;
365
366    (void)storage->pool->waitIdle(storage, lazy);
367}
368
369void *
370wsbmBOMap(struct _WsbmBufferObject *buf, unsigned mode)
371{
372    struct _WsbmBufStorage *storage = buf->storage;
373    void *virtual;
374    int retval;
375
376    retval = storage->pool->map(storage, mode, &virtual);
377
378    return (retval == 0) ? virtual : NULL;
379}
380
381void
382wsbmBOUnmap(struct _WsbmBufferObject *buf)
383{
384    struct _WsbmBufStorage *storage = buf->storage;
385
386    if (!storage)
387	return;
388
389    storage->pool->unmap(storage);
390}
391
392int
393wsbmBOSyncForCpu(struct _WsbmBufferObject *buf, unsigned mode)
394{
395    struct _WsbmBufStorage *storage = buf->storage;
396
397    return storage->pool->syncforcpu(storage, mode);
398}
399
400void
401wsbmBOReleaseFromCpu(struct _WsbmBufferObject *buf, unsigned mode)
402{
403    struct _WsbmBufStorage *storage = buf->storage;
404
405    storage->pool->releasefromcpu(storage, mode);
406}
407
408unsigned long
409wsbmBOOffsetHint(struct _WsbmBufferObject *buf)
410{
411    struct _WsbmBufStorage *storage = buf->storage;
412
413    return storage->pool->offset(storage);
414}
415
416unsigned long
417wsbmBOPoolOffset(struct _WsbmBufferObject *buf)
418{
419    struct _WsbmBufStorage *storage = buf->storage;
420
421    return storage->pool->poolOffset(storage);
422}
423
424uint32_t
425wsbmBOPlacementHint(struct _WsbmBufferObject * buf)
426{
427    struct _WsbmBufStorage *storage = buf->storage;
428
429    assert(buf->storage != NULL);
430
431    return storage->pool->placement(storage);
432}
433
434struct _WsbmBufferObject *
435wsbmBOReference(struct _WsbmBufferObject *buf)
436{
437    if (buf->bufferType == WSBM_BUFFER_SIMPLE) {
438	wsbmAtomicInc(&buf->storage->refCount);
439    } else {
440	wsbmAtomicInc(&buf->refCount);
441    }
442    return buf;
443}
444
445int
446wsbmBOSetStatus(struct _WsbmBufferObject *buf,
447		uint32_t setFlags, uint32_t clrFlags)
448{
449    struct _WsbmBufStorage *storage = buf->storage;
450
451    if (!storage)
452	return 0;
453
454    if (storage->pool->setStatus == NULL)
455	return -EINVAL;
456
457    return storage->pool->setStatus(storage, setFlags, clrFlags);
458}
459
460void
461wsbmBOUnreference(struct _WsbmBufferObject **p_buf)
462{
463    struct _WsbmBufferObject *buf = *p_buf;
464
465    *p_buf = NULL;
466
467    if (!buf)
468	return;
469
470    if (buf->bufferType == WSBM_BUFFER_SIMPLE) {
471	struct _WsbmBufStorage *dummy = buf->storage;
472
473	wsbmBufStorageUnref(&dummy);
474	return;
475    }
476
477    if (wsbmAtomicDecZero(&buf->refCount)) {
478	wsbmBufStorageUnref(&buf->storage);
479	free(buf);
480    }
481}
482
483int
484wsbmBOData(struct _WsbmBufferObject *buf,
485	   unsigned size, const void *data,
486	   struct _WsbmBufferPool *newPool, uint32_t placement)
487{
488    void *virtual = NULL;
489    int newBuffer;
490    int retval = 0;
491    struct _WsbmBufStorage *storage;
492    int synced = 0;
493    uint32_t placement_diff;
494    struct _WsbmBufferPool *curPool;
495
496    if (buf->bufferType == WSBM_BUFFER_SIMPLE)
497	return -EINVAL;
498
499    storage = buf->storage;
500
501    if (newPool == NULL)
502	newPool = buf->pool;
503
504    if (newPool == NULL)
505	return -EINVAL;
506
507    newBuffer = (!storage || storage->pool != newPool ||
508		 storage->pool->size(storage) < size ||
509		 storage->pool->size(storage) >
510		 size + WSBM_BODATA_SIZE_ACCEPT);
511
512    if (!placement)
513	placement = buf->placement;
514
515    if (newBuffer) {
516	if (buf->bufferType == WSBM_BUFFER_REF)
517	    return -EINVAL;
518
519	wsbmBufStorageUnref(&buf->storage);
520
521	if (size == 0) {
522	    buf->pool = newPool;
523	    buf->placement = placement;
524	    retval = 0;
525	    goto out;
526	}
527
528	buf->storage =
529	    newPool->create(newPool, size, placement, buf->alignment);
530	if (!buf->storage) {
531	    retval = -ENOMEM;
532	    goto out;
533	}
534
535	buf->placement = placement;
536	buf->pool = newPool;
537    } else if (wsbmAtomicRead(&storage->onList) ||
538	       0 != storage->pool->syncforcpu(storage, WSBM_SYNCCPU_WRITE |
539					      WSBM_SYNCCPU_DONT_BLOCK)) {
540	/*
541	 * Buffer is busy. need to create a new one.
542	 */
543
544	struct _WsbmBufStorage *tmp_storage;
545
546	curPool = storage->pool;
547
548	tmp_storage =
549	    curPool->create(curPool, size, placement, buf->alignment);
550
551	if (tmp_storage) {
552	    wsbmBufStorageUnref(&buf->storage);
553	    buf->storage = tmp_storage;
554	    buf->placement = placement;
555	} else {
556	    retval = curPool->syncforcpu(storage, WSBM_SYNCCPU_WRITE);
557	    if (retval)
558		goto out;
559	    synced = 1;
560	}
561    } else
562	synced = 1;
563
564    placement_diff = placement ^ buf->placement;
565
566    /*
567     * We might need to change buffer placement.
568     */
569
570    storage = buf->storage;
571    curPool = storage->pool;
572
573    if (placement_diff) {
574	assert(curPool->setStatus != NULL);
575	curPool->releasefromcpu(storage, WSBM_SYNCCPU_WRITE);
576	retval = curPool->setStatus(storage,
577				    placement_diff & placement,
578				    placement_diff & ~placement);
579	if (retval)
580	    goto out;
581
582	buf->placement = placement;
583
584    }
585
586    if (!synced) {
587	retval = curPool->syncforcpu(buf->storage, WSBM_SYNCCPU_WRITE);
588
589	if (retval)
590	    goto out;
591	synced = 1;
592    }
593
594    storage = buf->storage;
595    curPool = storage->pool;
596
597    if (data) {
598	retval = curPool->map(storage, WSBM_ACCESS_WRITE, &virtual);
599	if (retval)
600	    goto out;
601	memcpy(virtual, data, size);
602	curPool->unmap(storage);
603    }
604
605  out:
606
607    if (synced)
608	curPool->releasefromcpu(storage, WSBM_SYNCCPU_WRITE);
609
610    return retval;
611}
612
613int
614wsbmBODataUB(struct _WsbmBufferObject *buf,
615        unsigned size, const void *data, struct _WsbmBufferPool *newPool,
616        uint32_t placement, const unsigned long *user_ptr, int fd)
617{
618    int newBuffer;
619    int retval = 0;
620    struct _WsbmBufStorage *storage;
621    int synced = 0;
622    uint32_t placement_diff;
623    struct _WsbmBufferPool *curPool;
624    extern struct _WsbmBufStorage *
625    ttm_pool_ub_create(struct _WsbmBufferPool *pool,
626        unsigned long size, uint32_t placement, unsigned alignment,
627        const unsigned long *user_ptr, int fd);
628
629    if (buf->bufferType == WSBM_BUFFER_SIMPLE)
630        return -EINVAL;
631
632    storage = buf->storage;
633
634    if (newPool == NULL)
635        newPool = buf->pool;
636
637    if (newPool == NULL)
638        return -EINVAL;
639
640    newBuffer = (!storage || storage->pool != newPool ||
641        storage->pool->size(storage) < size ||
642        storage->pool->size(storage) >
643        size + WSBM_BODATA_SIZE_ACCEPT);
644
645    if (!placement)
646        placement = buf->placement;
647
648    if (newBuffer) {
649        if (buf->bufferType == WSBM_BUFFER_REF)
650            return -EINVAL;
651
652        wsbmBufStorageUnref(&buf->storage);
653
654        if (size == 0) {
655            buf->pool = newPool;
656            buf->placement = placement;
657            retval = 0;
658            goto out;
659        }
660
661        buf->storage =
662            ttm_pool_ub_create(newPool, size, placement, buf->alignment, user_ptr, fd);
663        if (!buf->storage) {
664            retval = -ENOMEM;
665            goto out;
666        }
667
668        buf->placement = placement;
669        buf->pool = newPool;
670    } else if (wsbmAtomicRead(&storage->onList) ||
671        0 != storage->pool->syncforcpu(storage, WSBM_SYNCCPU_WRITE |
672        WSBM_SYNCCPU_DONT_BLOCK)) {
673        /*
674        * Buffer is busy. need to create a new one.
675        * Actually such case will not be encountered for current ICS implementation
676        * TODO: maybe need refine the following code when such usage case is required
677        */
678
679        struct _WsbmBufStorage *tmp_storage;
680
681        curPool = storage->pool;
682
683        tmp_storage =
684            ttm_pool_ub_create(curPool, size, placement, buf->alignment, user_ptr, fd);
685
686        if (tmp_storage) {
687            wsbmBufStorageUnref(&buf->storage);
688            buf->storage = tmp_storage;
689            buf->placement = placement;
690        } else {
691            retval = curPool->syncforcpu(storage, WSBM_SYNCCPU_WRITE);
692            if (retval)
693                goto out;
694            synced = 1;
695        }
696    } else {
697        synced = 1;
698    }
699
700    placement_diff = placement ^ buf->placement;
701
702    /*
703    * We might need to change buffer placement.
704    */
705
706    storage = buf->storage;
707    curPool = storage->pool;
708
709    if (placement_diff) {
710        assert(curPool->setStatus != NULL);
711        curPool->releasefromcpu(storage, WSBM_SYNCCPU_WRITE);
712        retval = curPool->setStatus(storage,
713            placement_diff & placement,
714            placement_diff & ~placement);
715        if (retval)
716            goto out;
717
718        buf->placement = placement;
719    }
720
721    if (!synced) {
722        retval = curPool->syncforcpu(buf->storage, WSBM_SYNCCPU_WRITE);
723        if (retval)
724            goto out;
725        synced = 1;
726    }
727
728    storage = buf->storage;
729    curPool = storage->pool;
730
731    if (data) {
732        memcpy((unsigned long *) user_ptr, data, size);
733    }
734
735    out:
736
737    if (synced)
738        curPool->releasefromcpu(storage, WSBM_SYNCCPU_WRITE);
739
740    return retval;
741}
742
743static struct _WsbmBufStorage *
744wsbmStorageClone(struct _WsbmBufferObject *buf)
745{
746    struct _WsbmBufStorage *storage = buf->storage;
747    struct _WsbmBufferPool *pool = storage->pool;
748
749    return pool->create(pool, pool->size(storage), buf->placement,
750			buf->alignment);
751}
752
753struct _WsbmBufferObject *
754wsbmBOClone(struct _WsbmBufferObject *buf,
755	    int (*accelCopy) (struct _WsbmBufferObject *,
756			      struct _WsbmBufferObject *))
757{
758    struct _WsbmBufferObject *newBuf;
759    int ret;
760
761    newBuf = malloc(sizeof(*newBuf));
762    if (!newBuf)
763	return NULL;
764
765    *newBuf = *buf;
766    newBuf->storage = wsbmStorageClone(buf);
767    if (!newBuf->storage)
768	goto out_err0;
769
770    wsbmAtomicSet(&newBuf->refCount, 1);
771    if (!accelCopy || accelCopy(newBuf, buf) != 0) {
772
773	struct _WsbmBufferPool *pool = buf->storage->pool;
774	struct _WsbmBufStorage *storage = buf->storage;
775	struct _WsbmBufStorage *newStorage = newBuf->storage;
776	void *virtual;
777	void *nVirtual;
778
779	ret = pool->syncforcpu(storage, WSBM_SYNCCPU_READ);
780	if (ret)
781	    goto out_err1;
782	ret = pool->map(storage, WSBM_ACCESS_READ, &virtual);
783	if (ret)
784	    goto out_err2;
785	ret = pool->map(newStorage, WSBM_ACCESS_WRITE, &nVirtual);
786	if (ret)
787	    goto out_err3;
788
789	memcpy(nVirtual, virtual, pool->size(storage));
790	pool->unmap(newBuf->storage);
791	pool->unmap(buf->storage);
792	pool->releasefromcpu(storage, WSBM_SYNCCPU_READ);
793    }
794
795    return newBuf;
796  out_err3:
797    buf->pool->unmap(buf->storage);
798  out_err2:
799    buf->pool->releasefromcpu(buf->storage, WSBM_SYNCCPU_READ);
800  out_err1:
801    wsbmBufStorageUnref(&newBuf->storage);
802  out_err0:
803    free(newBuf);
804    return 0;
805}
806
807int
808wsbmBOSubData(struct _WsbmBufferObject *buf,
809	      unsigned long offset, unsigned long size, const void *data,
810	      int (*accelCopy) (struct _WsbmBufferObject *,
811				struct _WsbmBufferObject *))
812{
813    int ret = 0;
814
815    if (buf->bufferType == WSBM_BUFFER_SIMPLE)
816	return -EINVAL;
817
818    if (size && data) {
819	void *virtual;
820	struct _WsbmBufStorage *storage = buf->storage;
821	struct _WsbmBufferPool *pool = storage->pool;
822
823	ret = pool->syncforcpu(storage, WSBM_SYNCCPU_WRITE);
824	if (ret)
825	    goto out;
826
827	if (wsbmAtomicRead(&storage->onList)) {
828
829	    struct _WsbmBufferObject *newBuf;
830
831	    /*
832	     * Another context has this buffer on its validate list.
833	     * This should be a very rare situation, but it can be valid,
834	     * and therefore we must deal with it by cloning the storage.
835	     */
836
837	    pool->releasefromcpu(storage, WSBM_SYNCCPU_WRITE);
838	    newBuf = wsbmBOClone(buf, accelCopy);
839
840	    /*
841	     * If clone fails we have the choice of either bailing.
842	     * (The other context will be happy), or go on and update
843	     * the old buffer anyway. (We will be happy). We choose the
844	     * latter.
845	     */
846
847	    if (newBuf) {
848		storage = newBuf->storage;
849		wsbmAtomicInc(&storage->refCount);
850		wsbmBufStorageUnref(&buf->storage);
851		buf->storage = storage;
852		wsbmBOUnreference(&newBuf);
853		pool = storage->pool;
854	    }
855
856	    ret = pool->syncforcpu(storage, WSBM_SYNCCPU_WRITE);
857	    if (ret)
858		goto out;
859	}
860
861	ret = pool->map(storage, WSBM_ACCESS_WRITE, &virtual);
862	if (ret) {
863	    pool->releasefromcpu(storage, WSBM_SYNCCPU_WRITE);
864	    goto out;
865	}
866
867	memcpy((unsigned char *)virtual + offset, data, size);
868	pool->unmap(storage);
869	pool->releasefromcpu(storage, WSBM_SYNCCPU_WRITE);
870    }
871  out:
872    return ret;
873}
874
875int
876wsbmBOGetSubData(struct _WsbmBufferObject *buf,
877		 unsigned long offset, unsigned long size, void *data)
878{
879    int ret = 0;
880
881    if (size && data) {
882	void *virtual;
883	struct _WsbmBufStorage *storage = buf->storage;
884	struct _WsbmBufferPool *pool = storage->pool;
885
886	ret = pool->syncforcpu(storage, WSBM_SYNCCPU_READ);
887	if (ret)
888	    goto out;
889	ret = pool->map(storage, WSBM_ACCESS_READ, &virtual);
890	if (ret) {
891	    pool->releasefromcpu(storage, WSBM_SYNCCPU_WRITE);
892	    goto out;
893	}
894	memcpy(data, (unsigned char *)virtual + offset, size);
895	pool->unmap(storage);
896	pool->releasefromcpu(storage, WSBM_SYNCCPU_WRITE);
897    }
898  out:
899    return ret;
900}
901
902int
903wsbmBOSetReferenced(struct _WsbmBufferObject *buf, unsigned long handle)
904{
905    int ret = 0;
906
907    wsbmBufStorageUnref(&buf->storage);
908    if (buf->pool->createByReference == NULL) {
909	ret = -EINVAL;
910	goto out;
911    }
912    buf->storage = buf->pool->createByReference(buf->pool, handle);
913    if (!buf->storage) {
914	ret = -EINVAL;
915	goto out;
916    }
917    buf->bufferType = WSBM_BUFFER_REF;
918  out:
919    return ret;
920}
921
922void
923wsbmBOFreeSimple(void *ptr)
924{
925    free(ptr);
926}
927
928struct _WsbmBufferObject *
929wsbmBOCreateSimple(struct _WsbmBufferPool *pool,
930		   unsigned long size,
931		   uint32_t placement,
932		   unsigned alignment, size_t extra_size, size_t * offset)
933{
934    struct _WsbmBufferObject *buf;
935    struct _WsbmBufStorage *storage;
936
937    *offset = (sizeof(*buf) + 15) & ~15;
938
939    if (extra_size) {
940	extra_size += *offset - sizeof(*buf);
941    }
942
943    buf = (struct _WsbmBufferObject *)calloc(1, sizeof(*buf) + extra_size);
944    if (!buf)
945	return NULL;
946
947    storage = pool->create(pool, size, placement, alignment);
948    if (!storage)
949	goto out_err0;
950
951    storage->destroyContainer = &wsbmBOFreeSimple;
952    storage->destroyArg = buf;
953
954    buf->storage = storage;
955    buf->alignment = alignment;
956    buf->pool = pool;
957    buf->placement = placement;
958    buf->bufferType = WSBM_BUFFER_SIMPLE;
959
960    return buf;
961
962  out_err0:
963    free(buf);
964    return NULL;
965}
966
967int
968wsbmGenBuffers(struct _WsbmBufferPool *pool,
969	       unsigned n,
970	       struct _WsbmBufferObject *buffers[],
971	       unsigned alignment, uint32_t placement)
972{
973    struct _WsbmBufferObject *buf;
974    unsigned int i;
975
976    placement = (placement) ? placement :
977	WSBM_PL_FLAG_SYSTEM | WSBM_PL_FLAG_CACHED;
978
979    for (i = 0; i < n; ++i) {
980	buf = (struct _WsbmBufferObject *)calloc(1, sizeof(*buf));
981	if (!buf)
982	    return -ENOMEM;
983
984	wsbmAtomicSet(&buf->refCount, 1);
985	buf->placement = placement;
986	buf->alignment = alignment;
987	buf->pool = pool;
988	buf->bufferType = WSBM_BUFFER_COMPLEX;
989	buffers[i] = buf;
990    }
991    return 0;
992}
993
994void
995wsbmDeleteBuffers(unsigned n, struct _WsbmBufferObject *buffers[])
996{
997    unsigned int i;
998
999    for (i = 0; i < n; ++i) {
1000	wsbmBOUnreference(&buffers[i]);
1001    }
1002}
1003
1004/*
1005 * Note that lists are per-context and don't need mutex protection.
1006 */
1007
1008struct _WsbmBufferList *
1009wsbmBOCreateList(int target, int hasKernelBuffers)
1010{
1011    struct _WsbmBufferList *list = calloc(sizeof(*list), 1);
1012    int ret;
1013
1014    if (!list)
1015        return NULL;
1016    list->hasKernelBuffers = hasKernelBuffers;
1017    if (hasKernelBuffers) {
1018	ret = validateCreateList(target, &list->kernelBuffers, 0);
1019	if (ret)
1020	    return NULL;
1021    }
1022
1023    ret = validateCreateList(target, &list->userBuffers, 1);
1024    if (ret) {
1025	validateFreeList(&list->kernelBuffers);
1026	return NULL;
1027    }
1028
1029    return list;
1030}
1031
1032int
1033wsbmBOResetList(struct _WsbmBufferList *list)
1034{
1035    int ret;
1036
1037    if (list->hasKernelBuffers) {
1038	ret = validateResetList(&list->kernelBuffers);
1039	if (ret)
1040	    return ret;
1041    }
1042    ret = validateResetList(&list->userBuffers);
1043    return ret;
1044}
1045
1046void
1047wsbmBOFreeList(struct _WsbmBufferList *list)
1048{
1049    if (list->hasKernelBuffers)
1050	validateFreeList(&list->kernelBuffers);
1051    validateFreeList(&list->userBuffers);
1052    free(list);
1053}
1054
1055static int
1056wsbmAddValidateItem(struct _ValidateList *list, void *buf, uint64_t flags,
1057		    uint64_t mask, int *itemLoc,
1058		    struct _ValidateNode **pnode, int *newItem)
1059{
1060    struct _ValidateNode *node, *cur;
1061    struct _WsbmListHead *l;
1062    struct _WsbmListHead *hashHead;
1063    uint32_t hash;
1064    uint32_t count = 0;
1065    uint32_t key = (unsigned long) buf;
1066
1067    cur = NULL;
1068    hash = wsbmHashFunc((uint8_t *) &key, 4, list->hashMask);
1069    hashHead = list->hashTable + hash;
1070    *newItem = 0;
1071
1072    for (l = hashHead->next; l != hashHead; l = l->next) {
1073        count++;
1074	node = WSBMLISTENTRY(l, struct _ValidateNode, hashHead);
1075
1076	if (node->buf == buf) {
1077	    cur = node;
1078	    break;
1079	}
1080    }
1081
1082    if (!cur) {
1083	cur = validateListAddNode(list, buf, hash, flags, mask);
1084	if (!cur)
1085	    return -ENOMEM;
1086	*newItem = 1;
1087	cur->func->clear(cur);
1088    } else {
1089	uint64_t set_flags = flags & mask;
1090	uint64_t clr_flags = (~flags) & mask;
1091
1092	if (((cur->clr_flags | clr_flags) & WSBM_PL_MASK_MEM) ==
1093	    WSBM_PL_MASK_MEM) {
1094	    /*
1095	     * No available memory type left. Bail.
1096	     */
1097	    return -EINVAL;
1098	}
1099
1100	if ((cur->set_flags | set_flags) &
1101	    (cur->clr_flags | clr_flags) & ~WSBM_PL_MASK_MEM) {
1102	    /*
1103	     * Conflicting flags. Bail.
1104	     */
1105	    return -EINVAL;
1106	}
1107
1108	cur->set_flags &= ~(clr_flags & WSBM_PL_MASK_MEM);
1109	cur->set_flags |= (set_flags & ~WSBM_PL_MASK_MEM);
1110	cur->clr_flags |= clr_flags;
1111    }
1112    *itemLoc = cur->listItem;
1113    if (pnode)
1114	*pnode = cur;
1115    return 0;
1116}
1117
1118int
1119wsbmBOAddListItem(struct _WsbmBufferList *list,
1120		  struct _WsbmBufferObject *buf,
1121		  uint64_t flags, uint64_t mask, int *itemLoc,
1122		  struct _ValidateNode **node)
1123{
1124    int newItem;
1125    struct _WsbmBufStorage *storage = buf->storage;
1126    int ret;
1127    int dummy;
1128    struct _ValidateNode *dummyNode;
1129
1130    if (list->hasKernelBuffers) {
1131	ret = wsbmAddValidateItem(&list->kernelBuffers,
1132				  storage->pool->kernel(storage),
1133				  flags, mask, itemLoc, node, &dummy);
1134	if (ret)
1135	    goto out_unlock;
1136    } else {
1137	*node = NULL;
1138	*itemLoc = -1000;
1139    }
1140
1141    ret = wsbmAddValidateItem(&list->userBuffers, storage,
1142			      flags, mask, &dummy, &dummyNode, &newItem);
1143    if (ret)
1144	goto out_unlock;
1145
1146    if (newItem) {
1147	wsbmAtomicInc(&storage->refCount);
1148	wsbmAtomicInc(&storage->onList);
1149    }
1150
1151  out_unlock:
1152    return ret;
1153}
1154
1155void
1156wsbmBOFence(struct _WsbmBufferObject *buf, struct _WsbmFenceObject *fence)
1157{
1158    struct _WsbmBufStorage *storage;
1159
1160    storage = buf->storage;
1161    if (storage->pool->fence)
1162	storage->pool->fence(storage, fence);
1163
1164}
1165
1166int
1167wsbmBOOnList(const struct _WsbmBufferObject *buf)
1168{
1169    if (buf->storage == NULL)
1170	return 0;
1171    return wsbmAtomicRead(&buf->storage->onList);
1172}
1173
1174int
1175wsbmBOUnrefUserList(struct _WsbmBufferList *list)
1176{
1177    struct _WsbmBufStorage *storage;
1178    void *curBuf;
1179
1180    curBuf = validateListIterator(&list->userBuffers);
1181
1182    while (curBuf) {
1183	storage = (struct _WsbmBufStorage *)(validateListNode(curBuf)->buf);
1184	wsbmAtomicDec(&storage->onList);
1185	wsbmBufStorageUnref(&storage);
1186	curBuf = validateListNext(&list->userBuffers, curBuf);
1187    }
1188
1189    return wsbmBOResetList(list);
1190}
1191
1192
1193int
1194wsbmBOFenceUserList(struct _WsbmBufferList *list,
1195		    struct _WsbmFenceObject *fence)
1196{
1197    struct _WsbmBufStorage *storage;
1198    void *curBuf;
1199
1200    curBuf = validateListIterator(&list->userBuffers);
1201
1202    /*
1203     * User-space fencing callbacks.
1204     */
1205
1206    while (curBuf) {
1207	storage = (struct _WsbmBufStorage *)(validateListNode(curBuf)->buf);
1208
1209	storage->pool->fence(storage, fence);
1210	wsbmAtomicDec(&storage->onList);
1211	wsbmBufStorageUnref(&storage);
1212	curBuf = validateListNext(&list->userBuffers, curBuf);
1213    }
1214
1215    return wsbmBOResetList(list);
1216}
1217
1218int
1219wsbmBOValidateUserList(struct _WsbmBufferList *list)
1220{
1221    void *curBuf;
1222    struct _WsbmBufStorage *storage;
1223    struct _ValidateNode *node;
1224    int ret;
1225
1226    curBuf = validateListIterator(&list->userBuffers);
1227
1228    /*
1229     * User-space validation callbacks.
1230     */
1231
1232    while (curBuf) {
1233	node = validateListNode(curBuf);
1234	storage = (struct _WsbmBufStorage *)node->buf;
1235	if (storage->pool->validate) {
1236	    ret = storage->pool->validate(storage, node->set_flags,
1237					  node->clr_flags);
1238	    if (ret)
1239		return ret;
1240	}
1241	curBuf = validateListNext(&list->userBuffers, curBuf);
1242    }
1243    return 0;
1244}
1245
1246int
1247wsbmBOUnvalidateUserList(struct _WsbmBufferList *list)
1248{
1249    void *curBuf;
1250    struct _WsbmBufStorage *storage;
1251    struct _ValidateNode *node;
1252
1253    curBuf = validateListIterator(&list->userBuffers);
1254
1255    /*
1256     * User-space validation callbacks.
1257     */
1258
1259    while (curBuf) {
1260	node = validateListNode(curBuf);
1261	storage = (struct _WsbmBufStorage *)node->buf;
1262	if (storage->pool->unvalidate) {
1263	    storage->pool->unvalidate(storage);
1264	}
1265	wsbmAtomicDec(&storage->onList);
1266	wsbmBufStorageUnref(&storage);
1267	curBuf = validateListNext(&list->userBuffers, curBuf);
1268    }
1269    return wsbmBOResetList(list);
1270}
1271
1272void
1273wsbmPoolTakeDown(struct _WsbmBufferPool *pool)
1274{
1275    pool->takeDown(pool);
1276
1277}
1278
1279unsigned long
1280wsbmBOSize(struct _WsbmBufferObject *buf)
1281{
1282    unsigned long size;
1283    struct _WsbmBufStorage *storage;
1284
1285    storage = buf->storage;
1286    size = storage->pool->size(storage);
1287
1288    return size;
1289
1290}
1291
1292struct _ValidateList *
1293wsbmGetKernelValidateList(struct _WsbmBufferList *list)
1294{
1295    return (list->hasKernelBuffers) ? &list->kernelBuffers : NULL;
1296}
1297
1298struct _ValidateList *
1299wsbmGetUserValidateList(struct _WsbmBufferList *list)
1300{
1301    return &list->userBuffers;
1302}
1303
1304struct _ValidateNode *
1305validateListNode(void *iterator)
1306{
1307    struct _WsbmListHead *l = (struct _WsbmListHead *)iterator;
1308
1309    return WSBMLISTENTRY(l, struct _ValidateNode, head);
1310}
1311
1312void *
1313validateListIterator(struct _ValidateList *list)
1314{
1315    void *ret = list->list.next;
1316
1317    if (ret == &list->list)
1318	return NULL;
1319    return ret;
1320}
1321
1322void *
1323validateListNext(struct _ValidateList *list, void *iterator)
1324{
1325    void *ret;
1326
1327    struct _WsbmListHead *l = (struct _WsbmListHead *)iterator;
1328
1329    ret = l->next;
1330    if (ret == &list->list)
1331	return NULL;
1332    return ret;
1333}
1334
1335uint32_t
1336wsbmKBufHandle(const struct _WsbmKernelBuf * kBuf)
1337{
1338    return kBuf->handle;
1339}
1340
1341extern void
1342wsbmUpdateKBuf(struct _WsbmKernelBuf *kBuf,
1343	       uint64_t gpuOffset, uint32_t placement,
1344	       uint32_t fence_type_mask)
1345{
1346    kBuf->gpuOffset = gpuOffset;
1347    kBuf->placement = placement;
1348    kBuf->fence_type_mask = fence_type_mask;
1349}
1350
1351extern struct _WsbmKernelBuf *
1352wsbmKBuf(const struct _WsbmBufferObject *buf)
1353{
1354    struct _WsbmBufStorage *storage = buf->storage;
1355
1356    return storage->pool->kernel(storage);
1357}
1358