1 /**********************************************************
2  * Copyright 2009 VMware, Inc.  All rights reserved.
3  *
4  * Permission is hereby granted, free of charge, to any person
5  * obtaining a copy of this software and associated documentation
6  * files (the "Software"), to deal in the Software without
7  * restriction, including without limitation the rights to use, copy,
8  * modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  **********************************************************/
25 
26 
27 #include "svga_cmd.h"
28 
29 #include "util/u_debug.h"
30 #include "util/u_memory.h"
31 #include "util/u_debug_stack.h"
32 #include "pipebuffer/pb_buffer.h"
33 #include "pipebuffer/pb_validate.h"
34 
35 #include "svga_winsys.h"
36 #include "vmw_context.h"
37 #include "vmw_screen.h"
38 #include "vmw_buffer.h"
39 #include "vmw_surface.h"
40 #include "vmw_fence.h"
41 
42 #define VMW_COMMAND_SIZE (64*1024)
43 #define VMW_SURFACE_RELOCS (1024)
44 #define VMW_REGION_RELOCS (512)
45 
46 #define VMW_MUST_FLUSH_STACK 8
47 
48 struct vmw_region_relocation
49 {
50    struct SVGAGuestPtr *where;
51    struct pb_buffer *buffer;
52    /* TODO: put offset info inside where */
53    uint32 offset;
54 };
55 
56 struct vmw_svga_winsys_context
57 {
58    struct svga_winsys_context base;
59 
60    struct vmw_winsys_screen *vws;
61 
62 #ifdef DEBUG
63    boolean must_flush;
64    struct debug_stack_frame must_flush_stack[VMW_MUST_FLUSH_STACK];
65 #endif
66 
67    struct {
68       uint8_t buffer[VMW_COMMAND_SIZE];
69       uint32_t size;
70       uint32_t used;
71       uint32_t reserved;
72    } command;
73 
74    struct {
75       struct vmw_svga_winsys_surface *handles[VMW_SURFACE_RELOCS];
76       uint32_t size;
77       uint32_t used;
78       uint32_t staged;
79       uint32_t reserved;
80    } surface;
81 
82    struct {
83       struct vmw_region_relocation relocs[VMW_REGION_RELOCS];
84       uint32_t size;
85       uint32_t used;
86       uint32_t staged;
87       uint32_t reserved;
88    } region;
89 
90    struct pb_validate *validate;
91 
92    /**
93     * The amount of GMR that is referred by the commands currently batched
94     * in the context.
95     */
96    uint32_t seen_regions;
97 
98    /**
99     * Whether this context should fail to reserve more commands, not because it
100     * ran out of command space, but because a substantial ammount of GMR was
101     * referred.
102     */
103    boolean preemptive_flush;
104 };
105 
106 
107 static INLINE struct vmw_svga_winsys_context *
vmw_svga_winsys_context(struct svga_winsys_context * swc)108 vmw_svga_winsys_context(struct svga_winsys_context *swc)
109 {
110    assert(swc);
111    return (struct vmw_svga_winsys_context *)swc;
112 }
113 
114 
115 static INLINE unsigned
vmw_translate_to_pb_flags(unsigned flags)116 vmw_translate_to_pb_flags(unsigned flags)
117 {
118    unsigned f = 0;
119    if (flags & SVGA_RELOC_READ)
120       f |= PB_USAGE_GPU_READ;
121 
122    if (flags & SVGA_RELOC_WRITE)
123       f |= PB_USAGE_GPU_WRITE;
124 
125    return f;
126 }
127 
128 static enum pipe_error
vmw_swc_flush(struct svga_winsys_context * swc,struct pipe_fence_handle ** pfence)129 vmw_swc_flush(struct svga_winsys_context *swc,
130               struct pipe_fence_handle **pfence)
131 {
132    struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
133    struct pipe_fence_handle *fence = NULL;
134    unsigned i;
135    enum pipe_error ret;
136 
137    ret = pb_validate_validate(vswc->validate);
138    assert(ret == PIPE_OK);
139    if(ret == PIPE_OK) {
140 
141       /* Apply relocations */
142       for(i = 0; i < vswc->region.used; ++i) {
143          struct vmw_region_relocation *reloc = &vswc->region.relocs[i];
144          struct SVGAGuestPtr ptr;
145 
146          if(!vmw_gmr_bufmgr_region_ptr(reloc->buffer, &ptr))
147             assert(0);
148 
149          ptr.offset += reloc->offset;
150 
151          *reloc->where = ptr;
152       }
153 
154       if (vswc->command.used || pfence != NULL)
155          vmw_ioctl_command(vswc->vws,
156 			   vswc->base.cid,
157 			   0,
158                            vswc->command.buffer,
159                            vswc->command.used,
160                            &fence);
161 
162       pb_validate_fence(vswc->validate, fence);
163    }
164 
165    vswc->command.used = 0;
166    vswc->command.reserved = 0;
167 
168    for(i = 0; i < vswc->surface.used + vswc->surface.staged; ++i) {
169       struct vmw_svga_winsys_surface *vsurf =
170 	 vswc->surface.handles[i];
171       p_atomic_dec(&vsurf->validated);
172       vmw_svga_winsys_surface_reference(&vswc->surface.handles[i], NULL);
173    }
174 
175    vswc->surface.used = 0;
176    vswc->surface.reserved = 0;
177 
178    for(i = 0; i < vswc->region.used + vswc->region.staged; ++i) {
179       pb_reference(&vswc->region.relocs[i].buffer, NULL);
180    }
181 
182    vswc->region.used = 0;
183    vswc->region.reserved = 0;
184 
185 #ifdef DEBUG
186    vswc->must_flush = FALSE;
187 #endif
188    vswc->preemptive_flush = FALSE;
189    vswc->seen_regions = 0;
190 
191    if(pfence)
192       vmw_fence_reference(vswc->vws, pfence, fence);
193 
194    vmw_fence_reference(vswc->vws, &fence, NULL);
195 
196    return ret;
197 }
198 
199 
200 static void *
vmw_swc_reserve(struct svga_winsys_context * swc,uint32_t nr_bytes,uint32_t nr_relocs)201 vmw_swc_reserve(struct svga_winsys_context *swc,
202                 uint32_t nr_bytes, uint32_t nr_relocs )
203 {
204    struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
205 
206 #ifdef DEBUG
207    /* Check if somebody forgot to check the previous failure */
208    if(vswc->must_flush) {
209       debug_printf("Forgot to flush:\n");
210       debug_backtrace_dump(vswc->must_flush_stack, VMW_MUST_FLUSH_STACK);
211       assert(!vswc->must_flush);
212    }
213 #endif
214 
215    assert(nr_bytes <= vswc->command.size);
216    if(nr_bytes > vswc->command.size)
217       return NULL;
218 
219    if(vswc->preemptive_flush ||
220       vswc->command.used + nr_bytes > vswc->command.size ||
221       vswc->surface.used + nr_relocs > vswc->surface.size ||
222       vswc->region.used + nr_relocs > vswc->region.size) {
223 #ifdef DEBUG
224       vswc->must_flush = TRUE;
225       debug_backtrace_capture(vswc->must_flush_stack, 1,
226                               VMW_MUST_FLUSH_STACK);
227 #endif
228       return NULL;
229    }
230 
231    assert(vswc->command.used + nr_bytes <= vswc->command.size);
232    assert(vswc->surface.used + nr_relocs <= vswc->surface.size);
233    assert(vswc->region.used + nr_relocs <= vswc->region.size);
234 
235    vswc->command.reserved = nr_bytes;
236    vswc->surface.reserved = nr_relocs;
237    vswc->surface.staged = 0;
238    vswc->region.reserved = nr_relocs;
239    vswc->region.staged = 0;
240 
241    return vswc->command.buffer + vswc->command.used;
242 }
243 
244 
245 static void
vmw_swc_surface_relocation(struct svga_winsys_context * swc,uint32 * where,struct svga_winsys_surface * surface,unsigned flags)246 vmw_swc_surface_relocation(struct svga_winsys_context *swc,
247                            uint32 *where,
248                            struct svga_winsys_surface *surface,
249                            unsigned flags)
250 {
251    struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
252    struct vmw_svga_winsys_surface *vsurf;
253 
254    if(!surface) {
255       *where = SVGA3D_INVALID_ID;
256       return;
257    }
258 
259    assert(vswc->surface.staged < vswc->surface.reserved);
260 
261    vsurf = vmw_svga_winsys_surface(surface);
262 
263    *where = vsurf->sid;
264 
265    vmw_svga_winsys_surface_reference(&vswc->surface.handles[vswc->surface.used + vswc->surface.staged], vsurf);
266    p_atomic_inc(&vsurf->validated);
267    ++vswc->surface.staged;
268 }
269 
270 
271 static void
vmw_swc_region_relocation(struct svga_winsys_context * swc,struct SVGAGuestPtr * where,struct svga_winsys_buffer * buffer,uint32 offset,unsigned flags)272 vmw_swc_region_relocation(struct svga_winsys_context *swc,
273                           struct SVGAGuestPtr *where,
274                           struct svga_winsys_buffer *buffer,
275                           uint32 offset,
276                           unsigned flags)
277 {
278    struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
279    struct vmw_region_relocation *reloc;
280    unsigned translated_flags;
281    enum pipe_error ret;
282 
283    assert(vswc->region.staged < vswc->region.reserved);
284 
285    reloc = &vswc->region.relocs[vswc->region.used + vswc->region.staged];
286    reloc->where = where;
287    pb_reference(&reloc->buffer, vmw_pb_buffer(buffer));
288    reloc->offset = offset;
289 
290    ++vswc->region.staged;
291 
292    translated_flags = vmw_translate_to_pb_flags(flags);
293    ret = pb_validate_add_buffer(vswc->validate, reloc->buffer, translated_flags);
294    /* TODO: Update pipebuffer to reserve buffers and not fail here */
295    assert(ret == PIPE_OK);
296    (void)ret;
297 
298    /*
299     * Flush preemptively the FIFO commands to keep the GMR working set within
300     * the GMR pool size.
301     *
302     * This is necessary for applications like SPECviewperf that generate huge
303     * amounts of immediate vertex data, so that we don't pile up too much of
304     * that vertex data neither in the guest nor in the host.
305     *
306     * Note that in the current implementation if a region is referred twice in
307     * a command stream, it will be accounted twice. We could detect repeated
308     * regions and count only once, but there is no incentive to do that, since
309     * regions are typically short-lived; always referred in a single command;
310     * and at the worst we just flush the commands a bit sooner, which for the
311     * SVGA virtual device it's not a performance issue since flushing commands
312     * to the FIFO won't cause flushing in the host.
313     */
314    vswc->seen_regions += reloc->buffer->size;
315    if(vswc->seen_regions >= VMW_GMR_POOL_SIZE/3)
316       vswc->preemptive_flush = TRUE;
317 }
318 
319 
320 static void
vmw_swc_commit(struct svga_winsys_context * swc)321 vmw_swc_commit(struct svga_winsys_context *swc)
322 {
323    struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
324 
325    assert(vswc->command.reserved);
326    assert(vswc->command.used + vswc->command.reserved <= vswc->command.size);
327    vswc->command.used += vswc->command.reserved;
328    vswc->command.reserved = 0;
329 
330    assert(vswc->surface.staged <= vswc->surface.reserved);
331    assert(vswc->surface.used + vswc->surface.staged <= vswc->surface.size);
332    vswc->surface.used += vswc->surface.staged;
333    vswc->surface.staged = 0;
334    vswc->surface.reserved = 0;
335 
336    assert(vswc->region.staged <= vswc->region.reserved);
337    assert(vswc->region.used + vswc->region.staged <= vswc->region.size);
338    vswc->region.used += vswc->region.staged;
339    vswc->region.staged = 0;
340    vswc->region.reserved = 0;
341 }
342 
343 
344 static void
vmw_swc_destroy(struct svga_winsys_context * swc)345 vmw_swc_destroy(struct svga_winsys_context *swc)
346 {
347    struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
348    unsigned i;
349 
350    for(i = 0; i < vswc->region.used; ++i) {
351       pb_reference(&vswc->region.relocs[i].buffer, NULL);
352    }
353 
354    for(i = 0; i < vswc->surface.used; ++i) {
355       p_atomic_dec(&vswc->surface.handles[i]->validated);
356       vmw_svga_winsys_surface_reference(&vswc->surface.handles[i], NULL);
357    }
358    pb_validate_destroy(vswc->validate);
359    vmw_ioctl_context_destroy(vswc->vws, swc->cid);
360    FREE(vswc);
361 }
362 
363 
364 struct svga_winsys_context *
vmw_svga_winsys_context_create(struct svga_winsys_screen * sws)365 vmw_svga_winsys_context_create(struct svga_winsys_screen *sws)
366 {
367    struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
368    struct vmw_svga_winsys_context *vswc;
369 
370    vswc = CALLOC_STRUCT(vmw_svga_winsys_context);
371    if(!vswc)
372       return NULL;
373 
374    vswc->base.destroy = vmw_swc_destroy;
375    vswc->base.reserve = vmw_swc_reserve;
376    vswc->base.surface_relocation = vmw_swc_surface_relocation;
377    vswc->base.region_relocation = vmw_swc_region_relocation;
378    vswc->base.commit = vmw_swc_commit;
379    vswc->base.flush = vmw_swc_flush;
380 
381    vswc->base.cid = vmw_ioctl_context_create(vws);
382 
383    vswc->vws = vws;
384 
385    vswc->command.size = VMW_COMMAND_SIZE;
386    vswc->surface.size = VMW_SURFACE_RELOCS;
387    vswc->region.size = VMW_REGION_RELOCS;
388 
389    vswc->validate = pb_validate_create();
390    if(!vswc->validate) {
391       FREE(vswc);
392       return NULL;
393    }
394 
395    return &vswc->base;
396 }
397