diff --git a/drivers/base/reservation.c b/drivers/base/reservation.c index b82a5b6..c4bcf10 100644 --- a/drivers/base/reservation.c +++ b/drivers/base/reservation.c @@ -82,6 +82,8 @@ reservation_object_add_shared_inplace(struct reservation_object *obj, { u32 i; + preempt_disable(); + write_seqcount_begin(&obj->seq); for (i = 0; i < fobj->shared_count; ++i) { if (fobj->shared[i]->context == fence->context) { struct fence *old_fence = fobj->shared[i]; @@ -90,6 +92,8 @@ reservation_object_add_shared_inplace(struct reservation_object *obj, fobj->shared[i] = fence; + write_seqcount_end(&obj->seq); + preempt_enable(); fence_put(old_fence); return; } @@ -101,8 +105,9 @@ reservation_object_add_shared_inplace(struct reservation_object *obj, * make the new fence visible before incrementing * fobj->shared_count */ - smp_wmb(); fobj->shared_count++; + write_seqcount_end(&obj->seq); + preempt_enable(); } static void @@ -141,7 +146,11 @@ reservation_object_add_shared_replace(struct reservation_object *obj, fobj->shared[fobj->shared_count++] = fence; done: + preempt_disable(); + write_seqcount_begin(&obj->seq); obj->fence = fobj; + write_seqcount_end(&obj->seq); + preempt_enable(); kfree(old); } @@ -173,6 +182,8 @@ void reservation_object_add_excl_fence(struct reservation_object *obj, u32 i = 0; old = reservation_object_get_list(obj); + preempt_disable(); + write_seqcount_begin(&obj->seq); if (old) { i = old->shared_count; old->shared_count = 0; @@ -182,7 +193,8 @@ void reservation_object_add_excl_fence(struct reservation_object *obj, fence_get(fence); obj->fence_excl = fence; - + write_seqcount_end(&obj->seq); + preempt_enable(); /* inplace update, no shared fences */ while (i--) fence_put(old->shared[i]); @@ -191,3 +203,76 @@ void reservation_object_add_excl_fence(struct reservation_object *obj, fence_put(old_fence); } EXPORT_SYMBOL(reservation_object_add_excl_fence); + +struct unsignaled { + unsigned shared_max; + unsigned shared_count; + struct fence **shared; + struct fence *exclusive; +}; + +static int reservation_object_unsignaled_rcu(struct reservation_object *obj, + struct unsignaled *us) +{ + unsigned seq; + struct reservation_object_list *fobj, list; + struct fence *fence; + +retry: + seq = read_seqcount_begin(&obj->seq); + rcu_read_lock(); + + fobj = obj->fence; + fence = obj->exclusive; + + /* Check pointers for validity */ + if (read_seqcount_retry(&obj->seq, seq)) { + rcu_read_unlock(); + goto retry; + } + + list = *fobj; + + /* Check list for validity */ + if (read_seqcount_retry(&obj->seq, seq)) { + rcu_read_unlock(); + goto retry; + } + + if (list.shared_count == 0) { + if (fence && + !test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags) && + fence_get_rcu(fence)) + us->exclusive = exclusive; + rcu_read_unlock(); + return 0; + } + + + /* Needs reallocation? Either in this function or outside */ + if (us->shared_max < list.shared_count) { + rcu_read_unlock(); + return -ENOMEM; + } + + memcpy(us->shared, list.shared, + list.shared_count * sizeof(*list.shared)); + + /* Check the fence pointer array for validity */ + if (read_seqcount_retry(&obj->seq, seq)) { + rcu_read_unlock(); + goto retry; + } + + for (i = 0; i < list.shared_count; ++i) { + struct fence *fence = us->shared[i]; + + if (fence && !test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags) + && fence_get_rcu(fence)); + us->shared[us->shared_count++] = fence; + } + + rcu_read_unlock(); + + return 0; +} diff --git a/include/linux/reservation.h b/include/linux/reservation.h index b602365..4bf791a 100644 --- a/include/linux/reservation.h +++ b/include/linux/reservation.h @@ -52,6 +52,7 @@ struct reservation_object_list { struct reservation_object { struct ww_mutex lock; + struct seqcount seq; struct fence *fence_excl; struct reservation_object_list *fence; @@ -69,6 +70,7 @@ reservation_object_init(struct reservation_object *obj) obj->fence_excl = NULL; obj->fence = NULL; obj->staged = NULL; + seqcount_init(&obj->seq); } static inline void