| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: linux_dma_resv.c,v 1.21 2021/12/19 12:36:02 riastradh Exp $ */ | | 1 | /* $NetBSD: linux_dma_resv.c,v 1.22 2022/02/15 22:51:03 riastradh Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2018 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 2018 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation | | 7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Taylor R. Campbell. | | 8 | * by Taylor R. Campbell. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
| @@ -20,52 +20,63 @@ | | | @@ -20,52 +20,63 @@ |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | | 20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | | 21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | | 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. | | 29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | #include <sys/cdefs.h> | | 32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: linux_dma_resv.c,v 1.21 2021/12/19 12:36:02 riastradh Exp $"); | | 33 | __KERNEL_RCSID(0, "$NetBSD: linux_dma_resv.c,v 1.22 2022/02/15 22:51:03 riastradh Exp $"); |
34 | | | 34 | |
35 | #include <sys/param.h> | | 35 | #include <sys/param.h> |
36 | #include <sys/poll.h> | | 36 | #include <sys/poll.h> |
37 | #include <sys/select.h> | | 37 | #include <sys/select.h> |
38 | | | 38 | |
39 | #include <linux/dma-fence.h> | | 39 | #include <linux/dma-fence.h> |
40 | #include <linux/dma-resv.h> | | 40 | #include <linux/dma-resv.h> |
41 | #include <linux/seqlock.h> | | 41 | #include <linux/seqlock.h> |
42 | #include <linux/ww_mutex.h> | | 42 | #include <linux/ww_mutex.h> |
43 | | | 43 | |
44 | DEFINE_WW_CLASS(reservation_ww_class __cacheline_aligned); | | 44 | DEFINE_WW_CLASS(reservation_ww_class __cacheline_aligned); |
45 | | | 45 | |
46 | static struct dma_resv_list * | | 46 | static struct dma_resv_list * |
47 | objlist_tryalloc(uint32_t n) | | 47 | objlist_tryalloc(uint32_t n) |
48 | { | | 48 | { |
49 | struct dma_resv_list *list; | | 49 | struct dma_resv_list *list; |
50 | | | 50 | |
51 | list = kmem_alloc(offsetof(typeof(*list), shared[n]), KM_NOSLEEP); | | 51 | list = kmem_alloc(offsetof(typeof(*list), shared[n]), KM_NOSLEEP); |
52 | if (list == NULL) | | 52 | if (list == NULL) |
53 | return NULL; | | 53 | return NULL; |
54 | list->shared_max = n; | | 54 | list->shared_max = n; |
55 | | | 55 | |
56 | return list; | | 56 | return list; |
57 | } | | 57 | } |
58 | | | 58 | |
| | | 59 | static struct dma_resv_list * |
| | | 60 | objlist_alloc(uint32_t n) |
| | | 61 | { |
| | | 62 | struct dma_resv_list *list; |
| | | 63 | |
| | | 64 | list = kmem_alloc(offsetof(typeof(*list), shared[n]), KM_SLEEP); |
| | | 65 | list->shared_max = n; |
| | | 66 | |
| | | 67 | return list; |
| | | 68 | } |
| | | 69 | |
59 | static void | | 70 | static void |
60 | objlist_free(struct dma_resv_list *list) | | 71 | objlist_free(struct dma_resv_list *list) |
61 | { | | 72 | { |
62 | uint32_t n = list->shared_max; | | 73 | uint32_t n = list->shared_max; |
63 | | | 74 | |
64 | kmem_free(list, offsetof(typeof(*list), shared[n])); | | 75 | kmem_free(list, offsetof(typeof(*list), shared[n])); |
65 | } | | 76 | } |
66 | | | 77 | |
67 | static void | | 78 | static void |
68 | objlist_free_cb(struct rcu_head *rcu) | | 79 | objlist_free_cb(struct rcu_head *rcu) |
69 | { | | 80 | { |
70 | struct dma_resv_list *list = container_of(rcu, | | 81 | struct dma_resv_list *list = container_of(rcu, |
71 | struct dma_resv_list, rol_rcu); | | 82 | struct dma_resv_list, rol_rcu); |
| @@ -336,39 +347,36 @@ dma_resv_reserve_shared(struct dma_resv | | | @@ -336,39 +347,36 @@ dma_resv_reserve_shared(struct dma_resv |
336 | } else { | | 347 | } else { |
337 | /* No list already. We need space for num_fences. */ | | 348 | /* No list already. We need space for num_fences. */ |
338 | n = num_fences; | | 349 | n = num_fences; |
339 | } | | 350 | } |
340 | | | 351 | |
341 | /* If not, maybe there's a preallocated list ready. */ | | 352 | /* If not, maybe there's a preallocated list ready. */ |
342 | if (prealloc != NULL) { | | 353 | if (prealloc != NULL) { |
343 | /* If there's enough room in it, stop here. */ | | 354 | /* If there's enough room in it, stop here. */ |
344 | if (n <= prealloc->shared_max) | | 355 | if (n <= prealloc->shared_max) |
345 | return 0; | | 356 | return 0; |
346 | | | 357 | |
347 | /* Try to double its capacity. */ | | 358 | /* Try to double its capacity. */ |
348 | nalloc = n > UINT32_MAX/2 ? UINT32_MAX : 2*n; | | 359 | nalloc = n > UINT32_MAX/2 ? UINT32_MAX : 2*n; |
349 | prealloc = objlist_tryalloc(nalloc); | | 360 | prealloc = objlist_alloc(nalloc); |
350 | if (prealloc == NULL) | | | |
351 | return -ENOMEM; | | | |
352 | | | 361 | |
353 | /* Swap the new preallocated list and free the old one. */ | | 362 | /* Swap the new preallocated list and free the old one. */ |
354 | objlist_free(robj->robj_prealloc); | | 363 | objlist_free(robj->robj_prealloc); |
355 | robj->robj_prealloc = prealloc; | | 364 | robj->robj_prealloc = prealloc; |
356 | } else { | | 365 | } else { |
357 | /* Start with some spare. */ | | 366 | /* Start with some spare. */ |
358 | nalloc = n > UINT32_MAX/2 ? UINT32_MAX : MAX(2*n, 4); | | 367 | nalloc = n > UINT32_MAX/2 ? UINT32_MAX : MAX(2*n, 4); |
359 | prealloc = objlist_tryalloc(nalloc); | | 368 | prealloc = objlist_alloc(nalloc); |
360 | if (prealloc == NULL) | | 369 | |
361 | return -ENOMEM; | | | |
362 | /* Save the new preallocated list. */ | | 370 | /* Save the new preallocated list. */ |
363 | robj->robj_prealloc = prealloc; | | 371 | robj->robj_prealloc = prealloc; |
364 | } | | 372 | } |
365 | | | 373 | |
366 | /* Success! */ | | 374 | /* Success! */ |
367 | return 0; | | 375 | return 0; |
368 | } | | 376 | } |
369 | | | 377 | |
370 | struct dma_resv_write_ticket { | | 378 | struct dma_resv_write_ticket { |
371 | }; | | 379 | }; |
372 | | | 380 | |
373 | /* | | 381 | /* |
374 | * dma_resv_write_begin(robj, ticket) | | 382 | * dma_resv_write_begin(robj, ticket) |
| @@ -679,28 +687,30 @@ dma_resv_add_shared_fence(struct dma_res | | | @@ -679,28 +687,30 @@ dma_resv_add_shared_fence(struct dma_res |
679 | */ | | 687 | */ |
680 | for (i = 0; i < shared_count; i++) { | | 688 | for (i = 0; i < shared_count; i++) { |
681 | if (replace == NULL && | | 689 | if (replace == NULL && |
682 | list->shared[i]->context == fence->context) { | | 690 | list->shared[i]->context == fence->context) { |
683 | replace = list->shared[i]; | | 691 | replace = list->shared[i]; |
684 | prealloc->shared[i] = fence; | | 692 | prealloc->shared[i] = fence; |
685 | } else { | | 693 | } else { |
686 | prealloc->shared[i] = list->shared[i]; | | 694 | prealloc->shared[i] = list->shared[i]; |
687 | } | | 695 | } |
688 | } | | 696 | } |
689 | prealloc->shared_count = shared_count; | | 697 | prealloc->shared_count = shared_count; |
690 | | | 698 | |
691 | /* If we didn't find one, add it at the end. */ | | 699 | /* If we didn't find one, add it at the end. */ |
692 | if (replace == NULL) | | 700 | if (replace == NULL) { |
| | | 701 | KASSERT(prealloc->shared_count < prealloc->shared_max); |
693 | prealloc->shared[prealloc->shared_count++] = fence; | | 702 | prealloc->shared[prealloc->shared_count++] = fence; |
| | | 703 | } |
694 | | | 704 | |
695 | /* | | 705 | /* |
696 | * Now ready to replace the list. Begin an update. | | 706 | * Now ready to replace the list. Begin an update. |
697 | * Implies membar_producer for fence and prealloc. | | 707 | * Implies membar_producer for fence and prealloc. |
698 | */ | | 708 | */ |
699 | dma_resv_write_begin(robj, &ticket); | | 709 | dma_resv_write_begin(robj, &ticket); |
700 | | | 710 | |
701 | /* Replace the list. */ | | 711 | /* Replace the list. */ |
702 | atomic_store_relaxed(&robj->fence, prealloc); | | 712 | atomic_store_relaxed(&robj->fence, prealloc); |
703 | robj->robj_prealloc = NULL; | | 713 | robj->robj_prealloc = NULL; |
704 | | | 714 | |
705 | /* Commit the update. */ | | 715 | /* Commit the update. */ |
706 | dma_resv_write_commit(robj, &ticket); | | 716 | dma_resv_write_commit(robj, &ticket); |
| @@ -909,31 +919,40 @@ dma_resv_copy_fences(struct dma_resv *ds | | | @@ -909,31 +919,40 @@ dma_resv_copy_fences(struct dma_resv *ds |
909 | | | 919 | |
910 | KASSERT(dma_resv_held(dst_robj)); | | 920 | KASSERT(dma_resv_held(dst_robj)); |
911 | | | 921 | |
912 | top: KASSERT(fence == NULL); | | 922 | top: KASSERT(fence == NULL); |
913 | | | 923 | |
914 | /* Enter an RCU read section and get a read ticket. */ | | 924 | /* Enter an RCU read section and get a read ticket. */ |
915 | rcu_read_lock(); | | 925 | rcu_read_lock(); |
916 | dma_resv_read_begin(src_robj, &read_ticket); | | 926 | dma_resv_read_begin(src_robj, &read_ticket); |
917 | | | 927 | |
918 | /* Get the shared list. */ | | 928 | /* Get the shared list. */ |
919 | if (!dma_resv_get_shared_reader(src_robj, &src_list, &shared_count, | | 929 | if (!dma_resv_get_shared_reader(src_robj, &src_list, &shared_count, |
920 | &read_ticket)) | | 930 | &read_ticket)) |
921 | goto restart; | | 931 | goto restart; |
922 | if (src_list != NULL) { | | 932 | if (src_list) { |
923 | /* Allocate a new list. */ | | 933 | /* Allocate a new list, if necessary. */ |
924 | dst_list = objlist_tryalloc(shared_count); | | | |
925 | if (dst_list == NULL) | | 934 | if (dst_list == NULL) |
926 | return -ENOMEM; | | 935 | dst_list = objlist_tryalloc(shared_count); |
| | | 936 | if (dst_list == NULL || dst_list->shared_max < shared_count) { |
| | | 937 | rcu_read_unlock(); |
| | | 938 | if (dst_list) { |
| | | 939 | objlist_free(dst_list); |
| | | 940 | dst_list = NULL; |
| | | 941 | } |
| | | 942 | dst_list = objlist_alloc(shared_count); |
| | | 943 | dst_list->shared_count = 0; /* paranoia */ |
| | | 944 | goto top; |
| | | 945 | } |
927 | | | 946 | |
928 | /* Copy over all fences that are not yet signalled. */ | | 947 | /* Copy over all fences that are not yet signalled. */ |
929 | dst_list->shared_count = 0; | | 948 | dst_list->shared_count = 0; |
930 | for (i = 0; i < shared_count; i++) { | | 949 | for (i = 0; i < shared_count; i++) { |
931 | KASSERT(fence == NULL); | | 950 | KASSERT(fence == NULL); |
932 | fence = atomic_load_relaxed(&src_list->shared[i]); | | 951 | fence = atomic_load_relaxed(&src_list->shared[i]); |
933 | if ((fence = dma_fence_get_rcu(fence)) == NULL) | | 952 | if ((fence = dma_fence_get_rcu(fence)) == NULL) |
934 | goto restart; | | 953 | goto restart; |
935 | if (dma_fence_is_signaled(fence)) { | | 954 | if (dma_fence_is_signaled(fence)) { |
936 | dma_fence_put(fence); | | 955 | dma_fence_put(fence); |
937 | fence = NULL; | | 956 | fence = NULL; |
938 | continue; | | 957 | continue; |
939 | } | | 958 | } |
| @@ -995,28 +1014,27 @@ top: KASSERT(fence == NULL); | | | @@ -995,28 +1014,27 @@ top: KASSERT(fence == NULL); |
995 | } | | 1014 | } |
996 | | | 1015 | |
997 | /* Success! */ | | 1016 | /* Success! */ |
998 | return 0; | | 1017 | return 0; |
999 | | | 1018 | |
1000 | restart: | | 1019 | restart: |
1001 | KASSERT(fence == NULL); | | 1020 | KASSERT(fence == NULL); |
1002 | rcu_read_unlock(); | | 1021 | rcu_read_unlock(); |
1003 | if (dst_list) { | | 1022 | if (dst_list) { |
1004 | for (i = dst_list->shared_count; i --> 0;) { | | 1023 | for (i = dst_list->shared_count; i --> 0;) { |
1005 | dma_fence_put(dst_list->shared[i]); | | 1024 | dma_fence_put(dst_list->shared[i]); |
1006 | dst_list->shared[i] = NULL; /* paranoia */ | | 1025 | dst_list->shared[i] = NULL; /* paranoia */ |
1007 | } | | 1026 | } |
1008 | objlist_free(dst_list); | | 1027 | /* reuse dst_list allocation for the next attempt */ |
1009 | dst_list = NULL; | | | |
1010 | } | | 1028 | } |
1011 | goto top; | | 1029 | goto top; |
1012 | } | | 1030 | } |
1013 | | | 1031 | |
1014 | /* | | 1032 | /* |
1015 | * dma_resv_test_signaled_rcu(robj, shared) | | 1033 | * dma_resv_test_signaled_rcu(robj, shared) |
1016 | * | | 1034 | * |
1017 | * If shared is true, test whether all of the shared fences are | | 1035 | * If shared is true, test whether all of the shared fences are |
1018 | * signalled, or if there are none, test whether the exclusive | | 1036 | * signalled, or if there are none, test whether the exclusive |
1019 | * fence is signalled. If shared is false, test only whether the | | 1037 | * fence is signalled. If shared is false, test only whether the |
1020 | * exclusive fence is signalled. | | 1038 | * exclusive fence is signalled. |
1021 | * | | 1039 | * |
1022 | * XXX Why does this _not_ test the exclusive fence if shared is | | 1040 | * XXX Why does this _not_ test the exclusive fence if shared is |