2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus
18 Permission is hereby granted, free of charge, to any person obtaining
19 a copy of this software and associated documentation files (the
20 "Software"), to deal in the Software without restriction, including
21 without limitation the rights to use, copy, modify, merge, publish,
22 distribute, sublicense, and/or sell copies of the Software, and to
23 permit persons to whom the Software is furnished to do so, subject to
24 the following conditions:
26 The above copyright notice and this permission notice shall be
27 included in all copies or substantial portions of the Software.
29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 #include <vppinfra/longjmp.h>
39 #include <vppinfra/mheap.h>
40 #include <vppinfra/os.h>
42 void clib_smp_free (clib_smp_main_t * m)
44 clib_mem_vm_free (m->vm_base, (uword) ((1 + m->n_cpus) << m->log2_n_per_cpu_vm_bytes));
47 static uword allocate_per_cpu_mheap (uword cpu)
49 clib_smp_main_t * m = &clib_smp_main;
51 uword vm_size, stack_size, mheap_flags;
53 ASSERT (os_get_cpu_number () == cpu);
55 vm_size = (uword) 1 << m->log2_n_per_cpu_vm_bytes;
56 stack_size = (uword) 1 << m->log2_n_per_cpu_stack_bytes;
58 mheap_flags = MHEAP_FLAG_SMALL_OBJECT_CACHE;
60 /* Heap extends up to start of stack. */
61 heap = mheap_alloc_with_flags (clib_smp_vm_base_for_cpu (m, cpu),
64 clib_mem_set_heap (heap);
68 /* Now that we have a heap, allocate main structure on cpu 0. */
69 vec_resize (m->per_cpu_mains, m->n_cpus);
71 /* Allocate shared global heap (thread safe). */
73 mheap_alloc_with_flags (clib_smp_vm_base_for_cpu (m, cpu + m->n_cpus),
75 mheap_flags | MHEAP_FLAG_THREAD_SAFE);
78 m->per_cpu_mains[cpu].heap = heap;
82 void clib_smp_init (void)
84 clib_smp_main_t * m = &clib_smp_main;
87 m->vm_base = clib_mem_vm_alloc ((uword) (m->n_cpus + 1) << m->log2_n_per_cpu_vm_bytes);
89 clib_error ("error allocating virtual memory");
91 for (cpu = 0; cpu < m->n_cpus; cpu++)
92 clib_calljmp (allocate_per_cpu_mheap, cpu,
93 clib_smp_stack_top_for_cpu (m, cpu));
96 void clib_smp_lock_init (clib_smp_lock_t ** pl)
99 uword i, n_bytes, n_fifo_elts;
101 /* No locking necessary if n_cpus <= 1.
102 Null means no locking is necessary. */
103 if (clib_smp_main.n_cpus < 2)
109 /* Need n_cpus - 1 elts in waiting fifo. One CPU holds lock
110 and others could potentially be waiting. */
111 n_fifo_elts = clib_smp_main.n_cpus - 1;
113 n_bytes = sizeof (l[0]) + n_fifo_elts * sizeof (l->waiting_fifo[0]);
114 ASSERT_AND_PANIC (n_bytes % CLIB_CACHE_LINE_BYTES == 0);
116 l = clib_mem_alloc_aligned (n_bytes, CLIB_CACHE_LINE_BYTES);
118 memset (l, 0, n_bytes);
119 l->n_waiting_fifo_elts = n_fifo_elts;
121 for (i = 0; i < l->n_waiting_fifo_elts; i++)
122 l->waiting_fifo[i].wait_type = CLIB_SMP_LOCK_WAIT_EMPTY;
127 void clib_smp_lock_free (clib_smp_lock_t ** pl)
134 void clib_smp_lock_slow_path (clib_smp_lock_t * l,
136 clib_smp_lock_header_t h0,
137 clib_smp_lock_type_t type)
139 clib_smp_lock_header_t h1, h2, h3;
140 uword is_reader = type == CLIB_SMP_LOCK_TYPE_READER;
141 uword n_fifo_elts = l->n_waiting_fifo_elts;
144 /* Atomically advance waiting FIFO tail pointer; my_tail will point
145 to entry where we can insert ourselves to wait for lock to be granted. */
149 my_tail = h1.waiting_fifo.head_index + h1.waiting_fifo.n_elts;
150 my_tail = my_tail >= n_fifo_elts ? my_tail - n_fifo_elts : my_tail;
151 h1.waiting_fifo.n_elts += 1;
152 h1.request_cpu = my_cpu;
154 ASSERT_AND_PANIC (h1.waiting_fifo.n_elts <= n_fifo_elts);
155 ASSERT_AND_PANIC (my_tail >= 0 && my_tail < n_fifo_elts);
157 h2 = clib_smp_lock_set_header (l, h1, h0);
159 /* Tail successfully advanced? */
160 if (clib_smp_lock_header_is_equal (h0, h2))
163 /* It is possible that if head and tail are both zero, CPU with lock would have unlocked lock. */
164 else if (type == CLIB_SMP_LOCK_TYPE_SPIN)
166 while (! h2.writer_has_lock)
168 ASSERT_AND_PANIC (h2.waiting_fifo.n_elts == 0);
170 h1.request_cpu = my_cpu;
171 h1.writer_has_lock = 1;
173 h3 = clib_smp_lock_set_header (l, h1, h2);
176 if (clib_smp_lock_header_is_equal (h2, h3))
183 /* Try to advance tail again. */
188 clib_smp_lock_waiting_fifo_elt_t * w;
190 w = l->waiting_fifo + my_tail;
192 while (w->wait_type != CLIB_SMP_LOCK_WAIT_EMPTY)
195 w->wait_type = (is_reader
196 ? CLIB_SMP_LOCK_WAIT_READER
197 : CLIB_SMP_LOCK_WAIT_WRITER);
199 /* Wait until CPU holding the lock grants us the lock. */
200 while (w->wait_type != CLIB_SMP_LOCK_WAIT_DONE)
203 w->wait_type = CLIB_SMP_LOCK_WAIT_EMPTY;
207 void clib_smp_unlock_slow_path (clib_smp_lock_t * l,
209 clib_smp_lock_header_t h0,
210 clib_smp_lock_type_t type)
212 clib_smp_lock_header_t h1, h2;
213 clib_smp_lock_waiting_fifo_elt_t * head;
214 clib_smp_lock_wait_type_t head_wait_type;
215 uword is_reader = type == CLIB_SMP_LOCK_TYPE_READER;
216 uword n_fifo_elts = l->n_waiting_fifo_elts;
217 uword head_index, must_wait_for_readers;
221 /* Advance waiting fifo giving lock to first waiter. */
224 ASSERT_AND_PANIC (h0.waiting_fifo.n_elts != 0);
228 head_index = h1.waiting_fifo.head_index;
229 head = l->waiting_fifo + head_index;
232 ASSERT_AND_PANIC (h1.n_readers_with_lock > 0);
233 h1.n_readers_with_lock -= 1;
237 /* Writer will already have lock. */
238 ASSERT_AND_PANIC (h1.writer_has_lock);
241 while ((head_wait_type = head->wait_type) == CLIB_SMP_LOCK_WAIT_EMPTY)
244 /* Don't advance FIFO to writer unless all readers have unlocked. */
245 must_wait_for_readers =
246 (type != CLIB_SMP_LOCK_TYPE_SPIN
247 && head_wait_type == CLIB_SMP_LOCK_WAIT_WRITER
248 && h1.n_readers_with_lock != 0);
250 if (! must_wait_for_readers)
253 h1.waiting_fifo.n_elts -= 1;
254 if (type != CLIB_SMP_LOCK_TYPE_SPIN)
256 if (head_wait_type == CLIB_SMP_LOCK_WAIT_WRITER)
257 h1.writer_has_lock = h1.n_readers_with_lock == 0;
260 h1.writer_has_lock = 0;
261 h1.n_readers_with_lock += 1;
266 h1.waiting_fifo.head_index = head_index == n_fifo_elts ? 0 : head_index;
267 h1.request_cpu = my_cpu;
269 ASSERT_AND_PANIC (h1.waiting_fifo.head_index >= 0 && h1.waiting_fifo.head_index < n_fifo_elts);
270 ASSERT_AND_PANIC (h1.waiting_fifo.n_elts >= 0 && h1.waiting_fifo.n_elts <= n_fifo_elts);
272 h2 = clib_smp_lock_set_header (l, h1, h0);
274 if (clib_smp_lock_header_is_equal (h2, h0))
279 if (h0.waiting_fifo.n_elts == 0)
280 return clib_smp_unlock_inline (l, type);
283 if (must_wait_for_readers)
286 /* Wake up head of waiting fifo. */
290 /* Shift lock to first thread waiting in fifo. */
291 head->wait_type = CLIB_SMP_LOCK_WAIT_DONE;
293 /* For read locks we may be able to wake multiple readers. */
295 if (head_wait_type == CLIB_SMP_LOCK_WAIT_READER)
297 uword hi = h0.waiting_fifo.head_index;
298 if (h0.waiting_fifo.n_elts != 0
299 && l->waiting_fifo[hi].wait_type == CLIB_SMP_LOCK_WAIT_READER)