]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/drm2/ttm/ttm_bo_vm.c
Merge llvm trunk r300422 and resolve conflicts.
[FreeBSD/FreeBSD.git] / sys / dev / drm2 / ttm / ttm_bo_vm.c
1 /**************************************************************************
2  *
3  * Copyright (c) 2006-2009 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  * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
29  */
30 /*
31  * Copyright (c) 2013 The FreeBSD Foundation
32  * All rights reserved.
33  *
34  * Portions of this software were developed by Konstantin Belousov
35  * <kib@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
36  */
37
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40
41 #include "opt_vm.h"
42
43 #include <dev/drm2/drmP.h>
44 #include <dev/drm2/ttm/ttm_module.h>
45 #include <dev/drm2/ttm/ttm_bo_driver.h>
46 #include <dev/drm2/ttm/ttm_placement.h>
47
48 #include <vm/vm.h>
49 #include <vm/vm_page.h>
50 #include <vm/vm_pageout.h>
51
52 #define TTM_BO_VM_NUM_PREFAULT 16
53
54 RB_GENERATE(ttm_bo_device_buffer_objects, ttm_buffer_object, vm_rb,
55     ttm_bo_cmp_rb_tree_items);
56
57 int
58 ttm_bo_cmp_rb_tree_items(struct ttm_buffer_object *a,
59     struct ttm_buffer_object *b)
60 {
61
62         if (a->vm_node->start < b->vm_node->start) {
63                 return (-1);
64         } else if (a->vm_node->start > b->vm_node->start) {
65                 return (1);
66         } else {
67                 return (0);
68         }
69 }
70
71 static struct ttm_buffer_object *ttm_bo_vm_lookup_rb(struct ttm_bo_device *bdev,
72                                                      unsigned long page_start,
73                                                      unsigned long num_pages)
74 {
75         unsigned long cur_offset;
76         struct ttm_buffer_object *bo;
77         struct ttm_buffer_object *best_bo = NULL;
78
79         bo = RB_ROOT(&bdev->addr_space_rb);
80         while (bo != NULL) {
81                 cur_offset = bo->vm_node->start;
82                 if (page_start >= cur_offset) {
83                         best_bo = bo;
84                         if (page_start == cur_offset)
85                                 break;
86                         bo = RB_RIGHT(bo, vm_rb);
87                 } else
88                         bo = RB_LEFT(bo, vm_rb);
89         }
90
91         if (unlikely(best_bo == NULL))
92                 return NULL;
93
94         if (unlikely((best_bo->vm_node->start + best_bo->num_pages) <
95                      (page_start + num_pages)))
96                 return NULL;
97
98         return best_bo;
99 }
100
101 static int
102 ttm_bo_vm_fault(vm_object_t vm_obj, vm_ooffset_t offset,
103     int prot, vm_page_t *mres)
104 {
105
106         struct ttm_buffer_object *bo = vm_obj->handle;
107         struct ttm_bo_device *bdev = bo->bdev;
108         struct ttm_tt *ttm = NULL;
109         vm_page_t m, m1;
110         int ret;
111         int retval = VM_PAGER_OK;
112         struct ttm_mem_type_manager *man =
113                 &bdev->man[bo->mem.mem_type];
114
115         vm_object_pip_add(vm_obj, 1);
116         if (*mres != NULL) {
117                 vm_page_lock(*mres);
118                 vm_page_remove(*mres);
119                 vm_page_unlock(*mres);
120         }
121 retry:
122         VM_OBJECT_WUNLOCK(vm_obj);
123         m = NULL;
124
125 reserve:
126         ret = ttm_bo_reserve(bo, false, false, false, 0);
127         if (unlikely(ret != 0)) {
128                 if (ret == -EBUSY) {
129                         kern_yield(0);
130                         goto reserve;
131                 }
132         }
133
134         if (bdev->driver->fault_reserve_notify) {
135                 ret = bdev->driver->fault_reserve_notify(bo);
136                 switch (ret) {
137                 case 0:
138                         break;
139                 case -EBUSY:
140                 case -ERESTARTSYS:
141                 case -EINTR:
142                         kern_yield(0);
143                         goto reserve;
144                 default:
145                         retval = VM_PAGER_ERROR;
146                         goto out_unlock;
147                 }
148         }
149
150         /*
151          * Wait for buffer data in transit, due to a pipelined
152          * move.
153          */
154
155         mtx_lock(&bdev->fence_lock);
156         if (test_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags)) {
157                 /*
158                  * Here, the behavior differs between Linux and FreeBSD.
159                  *
160                  * On Linux, the wait is interruptible (3rd argument to
161                  * ttm_bo_wait). There must be some mechanism to resume
162                  * page fault handling, once the signal is processed.
163                  *
164                  * On FreeBSD, the wait is uninteruptible. This is not a
165                  * problem as we can't end up with an unkillable process
166                  * here, because the wait will eventually time out.
167                  *
168                  * An example of this situation is the Xorg process
169                  * which uses SIGALRM internally. The signal could
170                  * interrupt the wait, causing the page fault to fail
171                  * and the process to receive SIGSEGV.
172                  */
173                 ret = ttm_bo_wait(bo, false, false, false);
174                 mtx_unlock(&bdev->fence_lock);
175                 if (unlikely(ret != 0)) {
176                         retval = VM_PAGER_ERROR;
177                         goto out_unlock;
178                 }
179         } else
180                 mtx_unlock(&bdev->fence_lock);
181
182         ret = ttm_mem_io_lock(man, true);
183         if (unlikely(ret != 0)) {
184                 retval = VM_PAGER_ERROR;
185                 goto out_unlock;
186         }
187         ret = ttm_mem_io_reserve_vm(bo);
188         if (unlikely(ret != 0)) {
189                 retval = VM_PAGER_ERROR;
190                 goto out_io_unlock;
191         }
192
193         /*
194          * Strictly, we're not allowed to modify vma->vm_page_prot here,
195          * since the mmap_sem is only held in read mode. However, we
196          * modify only the caching bits of vma->vm_page_prot and
197          * consider those bits protected by
198          * the bo->mutex, as we should be the only writers.
199          * There shouldn't really be any readers of these bits except
200          * within vm_insert_mixed()? fork?
201          *
202          * TODO: Add a list of vmas to the bo, and change the
203          * vma->vm_page_prot when the object changes caching policy, with
204          * the correct locks held.
205          */
206         if (!bo->mem.bus.is_iomem) {
207                 /* Allocate all page at once, most common usage */
208                 ttm = bo->ttm;
209                 if (ttm->bdev->driver->ttm_tt_populate(ttm)) {
210                         retval = VM_PAGER_ERROR;
211                         goto out_io_unlock;
212                 }
213         }
214
215         if (bo->mem.bus.is_iomem) {
216                 m = PHYS_TO_VM_PAGE(bo->mem.bus.base + bo->mem.bus.offset +
217                     offset);
218                 KASSERT((m->flags & PG_FICTITIOUS) != 0,
219                     ("physical address %#jx not fictitious",
220                     (uintmax_t)(bo->mem.bus.base + bo->mem.bus.offset
221                     + offset)));
222                 pmap_page_set_memattr(m, ttm_io_prot(bo->mem.placement));
223         } else {
224                 ttm = bo->ttm;
225                 m = ttm->pages[OFF_TO_IDX(offset)];
226                 if (unlikely(!m)) {
227                         retval = VM_PAGER_ERROR;
228                         goto out_io_unlock;
229                 }
230                 pmap_page_set_memattr(m,
231                     (bo->mem.placement & TTM_PL_FLAG_CACHED) ?
232                     VM_MEMATTR_WRITE_BACK : ttm_io_prot(bo->mem.placement));
233         }
234
235         VM_OBJECT_WLOCK(vm_obj);
236         if (vm_page_busied(m)) {
237                 vm_page_lock(m);
238                 VM_OBJECT_WUNLOCK(vm_obj);
239                 vm_page_busy_sleep(m, "ttmpbs", false);
240                 VM_OBJECT_WLOCK(vm_obj);
241                 ttm_mem_io_unlock(man);
242                 ttm_bo_unreserve(bo);
243                 goto retry;
244         }
245         m1 = vm_page_lookup(vm_obj, OFF_TO_IDX(offset));
246         if (m1 == NULL) {
247                 if (vm_page_insert(m, vm_obj, OFF_TO_IDX(offset))) {
248                         VM_OBJECT_WUNLOCK(vm_obj);
249                         VM_WAIT;
250                         VM_OBJECT_WLOCK(vm_obj);
251                         ttm_mem_io_unlock(man);
252                         ttm_bo_unreserve(bo);
253                         goto retry;
254                 }
255         } else {
256                 KASSERT(m == m1,
257                     ("inconsistent insert bo %p m %p m1 %p offset %jx",
258                     bo, m, m1, (uintmax_t)offset));
259         }
260         m->valid = VM_PAGE_BITS_ALL;
261         vm_page_xbusy(m);
262         if (*mres != NULL) {
263                 KASSERT(*mres != m, ("losing %p %p", *mres, m));
264                 vm_page_lock(*mres);
265                 vm_page_free(*mres);
266                 vm_page_unlock(*mres);
267         }
268         *mres = m;
269
270 out_io_unlock1:
271         ttm_mem_io_unlock(man);
272 out_unlock1:
273         ttm_bo_unreserve(bo);
274         vm_object_pip_wakeup(vm_obj);
275         return (retval);
276
277 out_io_unlock:
278         VM_OBJECT_WLOCK(vm_obj);
279         goto out_io_unlock1;
280
281 out_unlock:
282         VM_OBJECT_WLOCK(vm_obj);
283         goto out_unlock1;
284 }
285
286 static int
287 ttm_bo_vm_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
288     vm_ooffset_t foff, struct ucred *cred, u_short *color)
289 {
290
291         /*
292          * On Linux, a reference to the buffer object is acquired here.
293          * The reason is that this function is not called when the
294          * mmap() is initialized, but only when a process forks for
295          * instance. Therefore on Linux, the reference on the bo is
296          * acquired either in ttm_bo_mmap() or ttm_bo_vm_open(). It's
297          * then released in ttm_bo_vm_close().
298          *
299          * Here, this function is called during mmap() initialization.
300          * Thus, the reference acquired in ttm_bo_mmap_single() is
301          * sufficient.
302          */
303
304         *color = 0;
305         return (0);
306 }
307
308 static void
309 ttm_bo_vm_dtor(void *handle)
310 {
311         struct ttm_buffer_object *bo = handle;
312
313         ttm_bo_unref(&bo);
314 }
315
316 static struct cdev_pager_ops ttm_pager_ops = {
317         .cdev_pg_fault = ttm_bo_vm_fault,
318         .cdev_pg_ctor = ttm_bo_vm_ctor,
319         .cdev_pg_dtor = ttm_bo_vm_dtor
320 };
321
322 int
323 ttm_bo_mmap_single(struct ttm_bo_device *bdev, vm_ooffset_t *offset, vm_size_t size,
324     struct vm_object **obj_res, int nprot)
325 {
326         struct ttm_bo_driver *driver;
327         struct ttm_buffer_object *bo;
328         struct vm_object *vm_obj;
329         int ret;
330
331         rw_wlock(&bdev->vm_lock);
332         bo = ttm_bo_vm_lookup_rb(bdev, OFF_TO_IDX(*offset), OFF_TO_IDX(size));
333         if (likely(bo != NULL))
334                 refcount_acquire(&bo->kref);
335         rw_wunlock(&bdev->vm_lock);
336
337         if (unlikely(bo == NULL)) {
338                 printf("[TTM] Could not find buffer object to map\n");
339                 return (-EINVAL);
340         }
341
342         driver = bo->bdev->driver;
343         if (unlikely(!driver->verify_access)) {
344                 ret = -EPERM;
345                 goto out_unref;
346         }
347         ret = driver->verify_access(bo);
348         if (unlikely(ret != 0))
349                 goto out_unref;
350
351         vm_obj = cdev_pager_allocate(bo, OBJT_MGTDEVICE, &ttm_pager_ops,
352             size, nprot, 0, curthread->td_ucred);
353         if (vm_obj == NULL) {
354                 ret = -EINVAL;
355                 goto out_unref;
356         }
357         /*
358          * Note: We're transferring the bo reference to vm_obj->handle here.
359          */
360         *offset = 0;
361         *obj_res = vm_obj;
362         return 0;
363 out_unref:
364         ttm_bo_unref(&bo);
365         return ret;
366 }
367
368 void
369 ttm_bo_release_mmap(struct ttm_buffer_object *bo)
370 {
371         vm_object_t vm_obj;
372         vm_page_t m;
373         int i;
374
375         vm_obj = cdev_pager_lookup(bo);
376         if (vm_obj == NULL)
377                 return;
378
379         VM_OBJECT_WLOCK(vm_obj);
380 retry:
381         for (i = 0; i < bo->num_pages; i++) {
382                 m = vm_page_lookup(vm_obj, i);
383                 if (m == NULL)
384                         continue;
385                 if (vm_page_sleep_if_busy(m, "ttm_unm"))
386                         goto retry;
387                 cdev_pager_free_page(vm_obj, m);
388         }
389         VM_OBJECT_WUNLOCK(vm_obj);
390
391         vm_object_deallocate(vm_obj);
392 }
393
394 #if 0
395 int ttm_fbdev_mmap(struct vm_area_struct *vma, struct ttm_buffer_object *bo)
396 {
397         if (vma->vm_pgoff != 0)
398                 return -EACCES;
399
400         vma->vm_ops = &ttm_bo_vm_ops;
401         vma->vm_private_data = ttm_bo_reference(bo);
402         vma->vm_flags |= VM_IO | VM_MIXEDMAP | VM_DONTEXPAND;
403         return 0;
404 }
405
406 ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
407                   const char __user *wbuf, char __user *rbuf, size_t count,
408                   loff_t *f_pos, bool write)
409 {
410         struct ttm_buffer_object *bo;
411         struct ttm_bo_driver *driver;
412         struct ttm_bo_kmap_obj map;
413         unsigned long dev_offset = (*f_pos >> PAGE_SHIFT);
414         unsigned long kmap_offset;
415         unsigned long kmap_end;
416         unsigned long kmap_num;
417         size_t io_size;
418         unsigned int page_offset;
419         char *virtual;
420         int ret;
421         bool no_wait = false;
422         bool dummy;
423
424         read_lock(&bdev->vm_lock);
425         bo = ttm_bo_vm_lookup_rb(bdev, dev_offset, 1);
426         if (likely(bo != NULL))
427                 ttm_bo_reference(bo);
428         read_unlock(&bdev->vm_lock);
429
430         if (unlikely(bo == NULL))
431                 return -EFAULT;
432
433         driver = bo->bdev->driver;
434         if (unlikely(!driver->verify_access)) {
435                 ret = -EPERM;
436                 goto out_unref;
437         }
438
439         ret = driver->verify_access(bo, filp);
440         if (unlikely(ret != 0))
441                 goto out_unref;
442
443         kmap_offset = dev_offset - bo->vm_node->start;
444         if (unlikely(kmap_offset >= bo->num_pages)) {
445                 ret = -EFBIG;
446                 goto out_unref;
447         }
448
449         page_offset = *f_pos & ~PAGE_MASK;
450         io_size = bo->num_pages - kmap_offset;
451         io_size = (io_size << PAGE_SHIFT) - page_offset;
452         if (count < io_size)
453                 io_size = count;
454
455         kmap_end = (*f_pos + count - 1) >> PAGE_SHIFT;
456         kmap_num = kmap_end - kmap_offset + 1;
457
458         ret = ttm_bo_reserve(bo, true, no_wait, false, 0);
459
460         switch (ret) {
461         case 0:
462                 break;
463         case -EBUSY:
464                 ret = -EAGAIN;
465                 goto out_unref;
466         default:
467                 goto out_unref;
468         }
469
470         ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map);
471         if (unlikely(ret != 0)) {
472                 ttm_bo_unreserve(bo);
473                 goto out_unref;
474         }
475
476         virtual = ttm_kmap_obj_virtual(&map, &dummy);
477         virtual += page_offset;
478
479         if (write)
480                 ret = copy_from_user(virtual, wbuf, io_size);
481         else
482                 ret = copy_to_user(rbuf, virtual, io_size);
483
484         ttm_bo_kunmap(&map);
485         ttm_bo_unreserve(bo);
486         ttm_bo_unref(&bo);
487
488         if (unlikely(ret != 0))
489                 return -EFBIG;
490
491         *f_pos += io_size;
492
493         return io_size;
494 out_unref:
495         ttm_bo_unref(&bo);
496         return ret;
497 }
498
499 ssize_t ttm_bo_fbdev_io(struct ttm_buffer_object *bo, const char __user *wbuf,
500                         char __user *rbuf, size_t count, loff_t *f_pos,
501                         bool write)
502 {
503         struct ttm_bo_kmap_obj map;
504         unsigned long kmap_offset;
505         unsigned long kmap_end;
506         unsigned long kmap_num;
507         size_t io_size;
508         unsigned int page_offset;
509         char *virtual;
510         int ret;
511         bool no_wait = false;
512         bool dummy;
513
514         kmap_offset = (*f_pos >> PAGE_SHIFT);
515         if (unlikely(kmap_offset >= bo->num_pages))
516                 return -EFBIG;
517
518         page_offset = *f_pos & ~PAGE_MASK;
519         io_size = bo->num_pages - kmap_offset;
520         io_size = (io_size << PAGE_SHIFT) - page_offset;
521         if (count < io_size)
522                 io_size = count;
523
524         kmap_end = (*f_pos + count - 1) >> PAGE_SHIFT;
525         kmap_num = kmap_end - kmap_offset + 1;
526
527         ret = ttm_bo_reserve(bo, true, no_wait, false, 0);
528
529         switch (ret) {
530         case 0:
531                 break;
532         case -EBUSY:
533                 return -EAGAIN;
534         default:
535                 return ret;
536         }
537
538         ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map);
539         if (unlikely(ret != 0)) {
540                 ttm_bo_unreserve(bo);
541                 return ret;
542         }
543
544         virtual = ttm_kmap_obj_virtual(&map, &dummy);
545         virtual += page_offset;
546
547         if (write)
548                 ret = copy_from_user(virtual, wbuf, io_size);
549         else
550                 ret = copy_to_user(rbuf, virtual, io_size);
551
552         ttm_bo_kunmap(&map);
553         ttm_bo_unreserve(bo);
554         ttm_bo_unref(&bo);
555
556         if (unlikely(ret != 0))
557                 return ret;
558
559         *f_pos += io_size;
560
561         return io_size;
562 }
563 #endif