| /* |
| * 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. |
| */ |
| |
| |
| #include <vnet/vnet.h> |
| #include <vnet/mfib/mfib_signal.h> |
| #include <vppinfra/dlist.h> |
| |
| /** |
| * @brief Pool of signals |
| */ |
| static mfib_signal_t *mfib_signal_pool; |
| |
| /** |
| * @brief pool of dlist elements |
| */ |
| static dlist_elt_t *mfib_signal_dlist_pool; |
| |
| /** |
| * the list/set of interfaces with signals pending |
| */ |
| typedef struct mfib_signal_q_t_ |
| { |
| /** |
| * the dlist indext that is the head of the list |
| */ |
| u32 mip_head; |
| |
| /** |
| * Spin lock to protect the list |
| */ |
| int mip_lock; |
| } mfib_signal_q_t; |
| |
| /** |
| * @brief The pending queue of signals to deliver to the control plane |
| */ |
| static mfib_signal_q_t mfib_signal_pending ; |
| |
| static void |
| mfib_signal_list_init (void) |
| { |
| dlist_elt_t *head; |
| u32 hi; |
| |
| pool_get(mfib_signal_dlist_pool, head); |
| hi = head - mfib_signal_dlist_pool; |
| |
| mfib_signal_pending.mip_head = hi; |
| clib_dlist_init(mfib_signal_dlist_pool, hi); |
| } |
| |
| void |
| mfib_signal_module_init (void) |
| { |
| mfib_signal_list_init(); |
| } |
| |
| static inline void |
| mfib_signal_lock_aquire (void) |
| { |
| while (clib_atomic_test_and_set (&mfib_signal_pending.mip_lock)) |
| ; |
| } |
| |
| static inline void |
| mfib_signal_lock_release (void) |
| { |
| clib_atomic_release(&mfib_signal_pending.mip_lock); |
| } |
| |
| #define MFIB_SIGNAL_CRITICAL_SECTION(_body) \ |
| { \ |
| mfib_signal_lock_aquire(); \ |
| do { \ |
| _body; \ |
| } while (0); \ |
| mfib_signal_lock_release(); \ |
| } |
| |
| int |
| mfib_signal_send_one (struct vl_api_registration_ *reg, |
| u32 context) |
| { |
| u32 li, si; |
| |
| /* |
| * with the lock held, pop a signal from the q. |
| */ |
| MFIB_SIGNAL_CRITICAL_SECTION( |
| ({ |
| li = clib_dlist_remove_head(mfib_signal_dlist_pool, |
| mfib_signal_pending.mip_head); |
| })); |
| |
| if (~0 != li) |
| { |
| mfib_signal_t *mfs; |
| mfib_itf_t *mfi; |
| dlist_elt_t *elt; |
| |
| elt = pool_elt_at_index(mfib_signal_dlist_pool, li); |
| si = elt->value; |
| |
| mfs = pool_elt_at_index(mfib_signal_pool, si); |
| mfi = mfib_itf_get(mfs->mfs_itf); |
| mfi->mfi_si = INDEX_INVALID; |
| clib_atomic_fetch_and(&mfi->mfi_flags, |
| ~MFIB_ITF_FLAG_SIGNAL_PRESENT); |
| |
| |
| vl_mfib_signal_send_one(reg, context, mfs); |
| |
| /* |
| * with the lock held, return the resoruces of the signals posted |
| */ |
| MFIB_SIGNAL_CRITICAL_SECTION( |
| ({ |
| pool_put_index(mfib_signal_pool, si); |
| pool_put_index(mfib_signal_dlist_pool, li); |
| })); |
| |
| return (1); |
| } |
| return (0); |
| } |
| |
| void |
| mfib_signal_push (const mfib_entry_t *mfe, |
| mfib_itf_t *mfi, |
| vlib_buffer_t *b0) |
| { |
| mfib_signal_t *mfs; |
| dlist_elt_t *elt; |
| u32 si, li; |
| |
| MFIB_SIGNAL_CRITICAL_SECTION( |
| ({ |
| pool_get(mfib_signal_pool, mfs); |
| pool_get(mfib_signal_dlist_pool, elt); |
| |
| si = mfs - mfib_signal_pool; |
| li = elt - mfib_signal_dlist_pool; |
| |
| elt->value = si; |
| mfi->mfi_si = li; |
| |
| clib_dlist_addhead(mfib_signal_dlist_pool, |
| mfib_signal_pending.mip_head, |
| li); |
| })); |
| |
| mfs->mfs_entry = mfib_entry_get_index(mfe); |
| mfs->mfs_itf = mfib_itf_get_index(mfi); |
| |
| if (NULL != b0) |
| { |
| mfs->mfs_buffer_len = b0->current_length; |
| memcpy(mfs->mfs_buffer, |
| vlib_buffer_get_current(b0), |
| (mfs->mfs_buffer_len > MFIB_SIGNAL_BUFFER_SIZE ? |
| MFIB_SIGNAL_BUFFER_SIZE : |
| mfs->mfs_buffer_len)); |
| } |
| else |
| { |
| mfs->mfs_buffer_len = 0; |
| } |
| } |
| |
| void |
| mfib_signal_remove_itf (const mfib_itf_t *mfi) |
| { |
| u32 li; |
| |
| /* |
| * lock the queue to prevent further additions while we fiddle. |
| */ |
| li = mfi->mfi_si; |
| |
| if (INDEX_INVALID != li) |
| { |
| /* |
| * it's in the pending q |
| */ |
| MFIB_SIGNAL_CRITICAL_SECTION( |
| ({ |
| dlist_elt_t *elt; |
| |
| /* |
| * with the lock held; |
| * - remove the signal from the pending list |
| * - free up the signal and list entry obejcts |
| */ |
| clib_dlist_remove(mfib_signal_dlist_pool, li); |
| |
| elt = pool_elt_at_index(mfib_signal_dlist_pool, li); |
| pool_put_index(mfib_signal_pool, elt->value); |
| pool_put(mfib_signal_dlist_pool, elt); |
| })); |
| } |
| } |