| /* |
| * Copyright (c) 2016 Cisco and/or its affiliates. |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at: |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * vlib provides lock-free counters but those |
| * - Have 16bits per-CPU counter, which may overflow. |
| * - Would only increment. |
| * |
| * This is very similar to vlib counters, but may be used to count reference. |
| * Such a counter includes an arbitrary number of counters. Each counter |
| * is identified by its index. This is used to aggregate per-cpu memory. |
| * |
| * Warning: |
| * This reference counter is lock-free but is not race-condition free. |
| * The counting result is approximate and another mechanism needs to be used |
| * in order to ensure that an object may be freed. |
| * |
| */ |
| |
| #include <vnet/vnet.h> |
| |
| /* |
| * Reference counting |
| * A specific reference counter is used. The design is quite |
| * similar to vlib counters but: |
| * - It is possible to decrease the value |
| * - Summing will not zero the per-thread counters |
| * - Only the thread can reallocate its own counters vector (to avoid concurrency issues) |
| */ |
| typedef struct { |
| u32 *counters; |
| volatile u32 *counter_lock; |
| CLIB_CACHE_LINE_ALIGN_MARK(o); |
| } vlib_refcount_per_cpu_t; |
| |
| typedef struct { |
| vlib_refcount_per_cpu_t *per_cpu; |
| } vlib_refcount_t; |
| |
| static_always_inline |
| void vlib_refcount_lock (volatile u32 *counter_lock) |
| { |
| while (clib_atomic_test_and_set (counter_lock)) |
| ; |
| } |
| |
| static_always_inline |
| void vlib_refcount_unlock (volatile u32 *counter_lock) |
| { |
| clib_atomic_release(counter_lock); |
| } |
| |
| void __vlib_refcount_resize(vlib_refcount_per_cpu_t *per_cpu, u32 size); |
| |
| static_always_inline |
| void vlib_refcount_add(vlib_refcount_t *r, u32 thread_index, u32 counter_index, i32 v) |
| { |
| vlib_refcount_per_cpu_t *per_cpu = &r->per_cpu[thread_index]; |
| if (PREDICT_FALSE(counter_index >= vec_len(per_cpu->counters))) |
| __vlib_refcount_resize(per_cpu, clib_max(counter_index + 16,(vec_len(per_cpu->counters)) * 2)); |
| |
| per_cpu->counters[counter_index] += v; |
| } |
| |
| u64 vlib_refcount_get(vlib_refcount_t *r, u32 index); |
| |
| static_always_inline |
| void vlib_refcount_init(vlib_refcount_t *r) |
| { |
| vlib_thread_main_t *tm = vlib_get_thread_main (); |
| u32 thread_index; |
| r->per_cpu = 0; |
| vec_validate (r->per_cpu, tm->n_vlib_mains - 1); |
| |
| for (thread_index = 0; thread_index < tm->n_vlib_mains; thread_index++) |
| { |
| r->per_cpu[thread_index].counter_lock = |
| clib_mem_alloc_aligned(CLIB_CACHE_LINE_BYTES,CLIB_CACHE_LINE_BYTES); |
| r->per_cpu[thread_index].counter_lock[0] = 0; |
| } |
| } |
| |
| |