resetting manifest requested domain to floor
[platform/upstream/libwsbm.git] / src / wsbm_ttmpool.c
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  */
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include <xf86drm.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <errno.h>
40 #include <sys/mman.h>
41 #include "wsbm_pool.h"
42 #include "assert.h"
43 #include "wsbm_priv.h"
44 #include "wsbm_manager.h"
45 #include "ttm/ttm_placement_user.h"
46
47 #define DRMRESTARTCOMMANDWRITE(_fd, _val, _arg, _ret)                   \
48         do {                                                            \
49                 (_ret) = drmCommandWrite(_fd, _val, &(_arg), sizeof(_arg)); \
50         } while ((_ret) == -EAGAIN || (_ret) == -ERESTART);             \
51
52 #define DRMRESTARTCOMMANDWRITEREAD(_fd, _val, _arg, _ret)               \
53         do {                                                            \
54                 (_ret) = drmCommandWriteRead(_fd, _val, &(_arg), sizeof(_arg)); \
55         } while ((_ret) == -EAGAIN || (_ret) == -ERESTART);             \
56
57 /*
58  * Buffer pool implementation using DRM buffer objects as wsbm buffer objects.
59  */
60
61 struct _TTMBuffer
62 {
63     struct _WsbmBufStorage buf;
64     struct _WsbmCond event;
65
66     /*
67      * Remains constant after creation.
68      */
69
70     uint64_t requestedSize;
71     uint64_t mapHandle;
72     uint64_t realSize;
73
74     /*
75      * Protected by the kernel lock.
76      */
77
78     struct _WsbmKernelBuf kBuf;
79
80     /*
81      * Protected by the mutex.
82      */
83
84     void *virtual;
85     int syncInProgress;
86     unsigned readers;
87     unsigned writers;
88 };
89
90 struct _TTMPool
91 {
92     struct _WsbmBufferPool pool;
93     unsigned int pageSize;
94     unsigned int devOffset;
95 };
96
97 static inline struct _TTMPool *
98 ttmGetPool(struct _TTMBuffer *dBuf)
99 {
100     return containerOf(dBuf->buf.pool, struct _TTMPool, pool);
101 }
102
103 static inline struct _TTMBuffer *
104 ttmBuffer(struct _WsbmBufStorage *buf)
105 {
106     return containerOf(buf, struct _TTMBuffer, buf);
107 }
108
109 static struct _WsbmBufStorage *
110 pool_create(struct _WsbmBufferPool *pool,
111             unsigned long size, uint32_t placement, unsigned alignment)
112 {
113     struct _TTMBuffer *dBuf = (struct _TTMBuffer *)
114         calloc(1, sizeof(*dBuf));
115     struct _TTMPool *ttmPool = containerOf(pool, struct _TTMPool, pool);
116     int ret;
117     unsigned pageSize = ttmPool->pageSize;
118     union ttm_pl_create_arg arg;
119
120     if (!dBuf)
121         return NULL;
122
123     if ((alignment > pageSize) && (alignment % pageSize))
124         goto out_err0;
125
126     ret = wsbmBufStorageInit(&dBuf->buf, pool);
127     if (ret)
128         goto out_err0;
129
130     ret = WSBM_COND_INIT(&dBuf->event);
131     if (ret)
132         goto out_err1;
133
134     arg.req.size = size;
135     arg.req.placement = placement;
136     arg.req.page_alignment = alignment / pageSize;
137
138     DRMRESTARTCOMMANDWRITEREAD(pool->fd, ttmPool->devOffset + TTM_PL_CREATE,
139                                arg, ret);
140
141     if (ret)
142         goto out_err2;
143
144     dBuf->requestedSize = size;
145     dBuf->kBuf.gpuOffset = arg.rep.gpu_offset;
146     dBuf->mapHandle = arg.rep.map_handle;
147     dBuf->realSize = arg.rep.bo_size;
148     dBuf->kBuf.placement = arg.rep.placement;
149     dBuf->kBuf.handle = arg.rep.handle;
150
151     return &dBuf->buf;
152
153   out_err2:
154     WSBM_COND_FREE(&dBuf->event);
155   out_err1:
156     wsbmBufStorageTakedown(&dBuf->buf);
157   out_err0:
158     free(dBuf);
159     return NULL;
160 }
161
162 static struct _WsbmBufStorage *
163 pool_reference(struct _WsbmBufferPool *pool, unsigned handle)
164 {
165     struct _TTMBuffer *dBuf = (struct _TTMBuffer *)calloc(1, sizeof(*dBuf));
166     struct _TTMPool *ttmPool = containerOf(pool, struct _TTMPool, pool);
167     union ttm_pl_reference_arg arg;
168     int ret;
169
170     if (!dBuf)
171         return NULL;
172
173     ret = wsbmBufStorageInit(&dBuf->buf, pool);
174     if (ret)
175         goto out_err0;
176
177     ret = WSBM_COND_INIT(&dBuf->event);
178     if (ret)
179         goto out_err1;
180
181     arg.req.handle = handle;
182     ret = drmCommandWriteRead(pool->fd, ttmPool->devOffset + TTM_PL_REFERENCE,
183                               &arg, sizeof(arg));
184
185     if (ret)
186         goto out_err2;
187
188     dBuf->requestedSize = arg.rep.bo_size;
189     dBuf->kBuf.gpuOffset = arg.rep.gpu_offset;
190     dBuf->mapHandle = arg.rep.map_handle;
191     dBuf->realSize = arg.rep.bo_size;
192     dBuf->kBuf.placement = arg.rep.placement;
193     dBuf->kBuf.handle = arg.rep.handle;
194     dBuf->kBuf.fence_type_mask = arg.rep.sync_object_arg;
195
196     return &dBuf->buf;
197
198   out_err2:
199     WSBM_COND_FREE(&dBuf->event);
200   out_err1:
201     wsbmBufStorageTakedown(&dBuf->buf);
202   out_err0:
203     free(dBuf);
204     return NULL;
205 }
206
207 static void
208 pool_destroy(struct _WsbmBufStorage **buf)
209 {
210     struct _TTMBuffer *dBuf = ttmBuffer(*buf);
211     struct _TTMPool *ttmPool = ttmGetPool(dBuf);
212     struct ttm_pl_reference_req arg;
213
214     *buf = NULL;
215     if (dBuf->virtual != NULL) {
216         (void)munmap(dBuf->virtual, dBuf->requestedSize);
217         dBuf->virtual = NULL;
218     }
219     arg.handle = dBuf->kBuf.handle;
220     (void)drmCommandWrite(dBuf->buf.pool->fd,
221                           ttmPool->devOffset + TTM_PL_UNREF,
222                           &arg, sizeof(arg));
223
224     WSBM_COND_FREE(&dBuf->event);
225     wsbmBufStorageTakedown(&dBuf->buf);
226     free(dBuf);
227 }
228
229 static int
230 syncforcpu_locked(struct _WsbmBufStorage *buf, unsigned mode)
231 {
232     uint32_t kmode = 0;
233     struct _TTMBuffer *dBuf = ttmBuffer(buf);
234     struct _TTMPool *ttmPool = ttmGetPool(dBuf);
235     unsigned int readers;
236     unsigned int writers;
237     int ret = 0;
238
239     while (dBuf->syncInProgress)
240         WSBM_COND_WAIT(&dBuf->event, &buf->mutex);
241
242     readers = dBuf->readers;
243     writers = dBuf->writers;
244
245     if ((mode & WSBM_SYNCCPU_READ) && (++dBuf->readers == 1))
246         kmode |= TTM_PL_SYNCCPU_MODE_READ;
247
248     if ((mode & WSBM_SYNCCPU_WRITE) && (++dBuf->writers == 1))
249         kmode |= TTM_PL_SYNCCPU_MODE_WRITE;
250
251     if (kmode) {
252         struct ttm_pl_synccpu_arg arg;
253
254         if (mode & WSBM_SYNCCPU_DONT_BLOCK)
255             kmode |= TTM_PL_SYNCCPU_MODE_NO_BLOCK;
256
257         dBuf->syncInProgress = 1;
258
259         /*
260          * This might be a lengthy wait, so
261          * release the mutex.
262          */
263
264         WSBM_MUTEX_UNLOCK(&buf->mutex);
265
266         arg.handle = dBuf->kBuf.handle;
267         arg.access_mode = kmode;
268         arg.op = TTM_PL_SYNCCPU_OP_GRAB;
269
270         DRMRESTARTCOMMANDWRITE(dBuf->buf.pool->fd,
271                                ttmPool->devOffset + TTM_PL_SYNCCPU, arg, ret);
272
273         WSBM_MUTEX_LOCK(&buf->mutex);
274         dBuf->syncInProgress = 0;
275         WSBM_COND_BROADCAST(&dBuf->event);
276
277         if (ret) {
278             dBuf->readers = readers;
279             dBuf->writers = writers;
280         }
281     }
282
283     return ret;
284 }
285
286 static int
287 releasefromcpu_locked(struct _WsbmBufStorage *buf, unsigned mode)
288 {
289     uint32_t kmode = 0;
290     struct _TTMBuffer *dBuf = ttmBuffer(buf);
291     struct _TTMPool *ttmPool = ttmGetPool(dBuf);
292     int ret = 0;
293
294     while (dBuf->syncInProgress)
295         WSBM_COND_WAIT(&dBuf->event, &buf->mutex);
296
297     if ((mode & WSBM_SYNCCPU_READ) && (--dBuf->readers == 0))
298         kmode |= TTM_PL_SYNCCPU_MODE_READ;
299
300     if ((mode & WSBM_SYNCCPU_WRITE) && (--dBuf->writers == 0))
301         kmode |= TTM_PL_SYNCCPU_MODE_WRITE;
302
303     if (kmode) {
304         struct ttm_pl_synccpu_arg arg;
305
306         arg.handle = dBuf->kBuf.handle;
307         arg.access_mode = kmode;
308         arg.op = TTM_PL_SYNCCPU_OP_RELEASE;
309
310         DRMRESTARTCOMMANDWRITE(dBuf->buf.pool->fd,
311                                ttmPool->devOffset + TTM_PL_SYNCCPU, arg, ret);
312
313     }
314
315     return ret;
316 }
317
318 static int
319 pool_syncforcpu(struct _WsbmBufStorage *buf, unsigned mode)
320 {
321     int ret;
322
323     WSBM_MUTEX_LOCK(&buf->mutex);
324     ret = syncforcpu_locked(buf, mode);
325     WSBM_MUTEX_UNLOCK(&buf->mutex);
326     return ret;
327 }
328
329 static void
330 pool_releasefromcpu(struct _WsbmBufStorage *buf, unsigned mode)
331 {
332     WSBM_MUTEX_LOCK(&buf->mutex);
333     (void)releasefromcpu_locked(buf, mode);
334     WSBM_MUTEX_UNLOCK(&buf->mutex);
335 }
336
337 static int
338 pool_map(struct _WsbmBufStorage *buf, unsigned mode, void **virtual)
339 {
340     struct _TTMBuffer *dBuf = ttmBuffer(buf);
341     void *virt;
342     int ret = 0;
343
344     WSBM_MUTEX_LOCK(&buf->mutex);
345
346     /*
347      * mmaps are expensive, so we only really unmap if
348      * we destroy the buffer.
349      */
350
351     if (dBuf->virtual == NULL) {
352         virt = mmap(0, dBuf->requestedSize,
353                     PROT_READ | PROT_WRITE, MAP_SHARED,
354                     buf->pool->fd, dBuf->mapHandle);
355         if (virt == MAP_FAILED) {
356             ret = -errno;
357             goto out_unlock;
358         }
359         dBuf->virtual = virt;
360     }
361
362     *virtual = dBuf->virtual;
363   out_unlock:
364
365     WSBM_MUTEX_UNLOCK(&buf->mutex);
366
367     return ret;
368 }
369
370 static void
371 pool_unmap(struct _WsbmBufStorage *buf)
372 {
373     ;
374 }
375
376 static unsigned long
377 pool_offset(struct _WsbmBufStorage *buf)
378 {
379     struct _TTMBuffer *dBuf = ttmBuffer(buf);
380
381     return dBuf->kBuf.gpuOffset;
382 }
383
384 static unsigned long
385 pool_poolOffset(struct _WsbmBufStorage *buf)
386 {
387     return 0;
388 }
389
390 static uint32_t
391 pool_placement(struct _WsbmBufStorage *buf)
392 {
393     struct _TTMBuffer *dBuf = ttmBuffer(buf);
394
395     return dBuf->kBuf.placement;
396 }
397
398 static unsigned long
399 pool_size(struct _WsbmBufStorage *buf)
400 {
401     struct _TTMBuffer *dBuf = ttmBuffer(buf);
402
403     return dBuf->realSize;
404 }
405
406 static void
407 pool_fence(struct _WsbmBufStorage *buf, struct _WsbmFenceObject *fence)
408 {
409     /*
410      * Noop. The kernel handles all fencing.
411      */
412 }
413
414 static int
415 pool_waitIdle(struct _WsbmBufStorage *buf, int lazy)
416 {
417     struct _TTMBuffer *dBuf = ttmBuffer(buf);
418     struct _TTMPool *ttmPool = ttmGetPool(dBuf);
419     struct ttm_pl_waitidle_arg req;
420     struct _WsbmBufferPool *pool = buf->pool;
421     int ret;
422
423     req.handle = dBuf->kBuf.handle;
424     req.mode = (lazy) ? TTM_PL_WAITIDLE_MODE_LAZY : 0;
425
426     DRMRESTARTCOMMANDWRITE(pool->fd, ttmPool->devOffset + TTM_PL_WAITIDLE,
427                            req, ret);
428
429     return ret;
430 }
431
432 static void
433 pool_takedown(struct _WsbmBufferPool *pool)
434 {
435     struct _TTMPool *ttmPool = containerOf(pool, struct _TTMPool, pool);
436
437     free(ttmPool);
438 }
439
440 static int
441 pool_setStatus(struct _WsbmBufStorage *buf, uint32_t set_placement,
442                uint32_t clr_placement)
443 {
444     struct _TTMBuffer *dBuf = ttmBuffer(buf);
445     struct _TTMPool *ttmPool = ttmGetPool(dBuf);
446     union ttm_pl_setstatus_arg arg;
447     struct ttm_pl_setstatus_req *req = &arg.req;
448     struct ttm_pl_rep *rep = &arg.rep;
449     struct _WsbmBufferPool *pool = buf->pool;
450     int ret;
451
452     req->handle = dBuf->kBuf.handle;
453     req->set_placement = set_placement;
454     req->clr_placement = clr_placement;
455
456     DRMRESTARTCOMMANDWRITEREAD(pool->fd,
457                                ttmPool->devOffset + TTM_PL_SETSTATUS,
458                                arg, ret);
459
460     if (!ret) {
461         dBuf->kBuf.gpuOffset = rep->gpu_offset;
462         dBuf->kBuf.placement = rep->placement;
463     }
464
465     return ret;
466 }
467
468 static struct _WsbmKernelBuf *
469 pool_kernel(struct _WsbmBufStorage *buf)
470 {
471     return (void *)&ttmBuffer(buf)->kBuf;
472 }
473
474 struct _WsbmBufferPool *
475 wsbmTTMPoolInit(int fd, unsigned int devOffset)
476 {
477     struct _TTMPool *ttmPool;
478     struct _WsbmBufferPool *pool;
479
480     ttmPool = (struct _TTMPool *)calloc(1, sizeof(*ttmPool));
481
482     if (!ttmPool)
483         return NULL;
484
485     ttmPool->pageSize = getpagesize();
486     ttmPool->devOffset = devOffset;
487     pool = &ttmPool->pool;
488
489     pool->fd = fd;
490     pool->map = &pool_map;
491     pool->unmap = &pool_unmap;
492     pool->syncforcpu = &pool_syncforcpu;
493     pool->releasefromcpu = &pool_releasefromcpu;
494     pool->destroy = &pool_destroy;
495     pool->offset = &pool_offset;
496     pool->poolOffset = &pool_poolOffset;
497     pool->placement = &pool_placement;
498     pool->size = &pool_size;
499     pool->create = &pool_create;
500     pool->fence = &pool_fence;
501     pool->kernel = &pool_kernel;
502     pool->validate = NULL;
503     pool->unvalidate = NULL;
504     pool->waitIdle = &pool_waitIdle;
505     pool->takeDown = &pool_takedown;
506     pool->createByReference = &pool_reference;
507     pool->setStatus = &pool_setStatus;
508     return pool;
509 }