| /* | |
| * net/tipc/ref.c: TIPC object registry code | |
| * | |
| * Copyright (c) 1991-2006, Ericsson AB | |
| * Copyright (c) 2004-2005, Wind River Systems | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are met: | |
| * | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * 3. Neither the names of the copyright holders nor the names of its | |
| * contributors may be used to endorse or promote products derived from | |
| * this software without specific prior written permission. | |
| * | |
| * Alternatively, this software may be distributed under the terms of the | |
| * GNU General Public License ("GPL") version 2 as published by the Free | |
| * Software Foundation. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| * POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| #include "core.h" | |
| #include "ref.h" | |
| #include "port.h" | |
| #include "subscr.h" | |
| #include "name_distr.h" | |
| #include "name_table.h" | |
| #include "config.h" | |
| #include "discover.h" | |
| #include "bearer.h" | |
| #include "node.h" | |
| #include "bcast.h" | |
| /* | |
| * Object reference table consists of 2**N entries. | |
| * | |
| * A used entry has object ptr != 0, reference == XXXX|own index | |
| * (XXXX changes each time entry is acquired) | |
| * A free entry has object ptr == 0, reference == YYYY|next free index | |
| * (YYYY is one more than last used XXXX) | |
| * | |
| * Free list is initially chained from entry (2**N)-1 to entry 1. | |
| * Entry 0 is not used to allow index 0 to indicate the end of the free list. | |
| * | |
| * Note: Any accidental reference of the form XXXX|0--0 won't match entry 0 | |
| * because entry 0's reference field has the form XXXX|1--1. | |
| */ | |
| struct ref_table tipc_ref_table = { NULL }; | |
| static DEFINE_RWLOCK(ref_table_lock); | |
| /** | |
| * tipc_ref_table_init - create reference table for objects | |
| */ | |
| int tipc_ref_table_init(u32 requested_size, u32 start) | |
| { | |
| struct reference *table; | |
| u32 sz = 1 << 4; | |
| u32 index_mask; | |
| int i; | |
| while (sz < requested_size) { | |
| sz <<= 1; | |
| } | |
| table = vmalloc(sz * sizeof(*table)); | |
| if (table == NULL) | |
| return -ENOMEM; | |
| write_lock_bh(&ref_table_lock); | |
| index_mask = sz - 1; | |
| for (i = sz - 1; i >= 0; i--) { | |
| table[i].object = NULL; | |
| spin_lock_init(&table[i].lock); | |
| table[i].data.next_plus_upper = (start & ~index_mask) + i - 1; | |
| } | |
| tipc_ref_table.entries = table; | |
| tipc_ref_table.index_mask = index_mask; | |
| tipc_ref_table.first_free = sz - 1; | |
| tipc_ref_table.last_free = 1; | |
| write_unlock_bh(&ref_table_lock); | |
| return TIPC_OK; | |
| } | |
| /** | |
| * tipc_ref_table_stop - destroy reference table for objects | |
| */ | |
| void tipc_ref_table_stop(void) | |
| { | |
| if (!tipc_ref_table.entries) | |
| return; | |
| vfree(tipc_ref_table.entries); | |
| tipc_ref_table.entries = NULL; | |
| } | |
| /** | |
| * tipc_ref_acquire - create reference to an object | |
| * | |
| * Return a unique reference value which can be translated back to the pointer | |
| * 'object' at a later time. Also, pass back a pointer to the lock protecting | |
| * the object, but without locking it. | |
| */ | |
| u32 tipc_ref_acquire(void *object, spinlock_t **lock) | |
| { | |
| struct reference *entry; | |
| u32 index; | |
| u32 index_mask; | |
| u32 next_plus_upper; | |
| u32 reference = 0; | |
| if (!object) { | |
| err("Attempt to acquire reference to non-existent object\n"); | |
| return 0; | |
| } | |
| if (!tipc_ref_table.entries) { | |
| err("Reference table not found during acquisition attempt\n"); | |
| return 0; | |
| } | |
| write_lock_bh(&ref_table_lock); | |
| if (tipc_ref_table.first_free) { | |
| index = tipc_ref_table.first_free; | |
| entry = &(tipc_ref_table.entries[index]); | |
| index_mask = tipc_ref_table.index_mask; | |
| /* take lock in case a previous user of entry still holds it */ | |
| spin_lock_bh(&entry->lock); | |
| next_plus_upper = entry->data.next_plus_upper; | |
| tipc_ref_table.first_free = next_plus_upper & index_mask; | |
| reference = (next_plus_upper & ~index_mask) + index; | |
| entry->data.reference = reference; | |
| entry->object = object; | |
| if (lock != 0) | |
| *lock = &entry->lock; | |
| spin_unlock_bh(&entry->lock); | |
| } | |
| write_unlock_bh(&ref_table_lock); | |
| return reference; | |
| } | |
| /** | |
| * tipc_ref_discard - invalidate references to an object | |
| * | |
| * Disallow future references to an object and free up the entry for re-use. | |
| * Note: The entry's spin_lock may still be busy after discard | |
| */ | |
| void tipc_ref_discard(u32 ref) | |
| { | |
| struct reference *entry; | |
| u32 index; | |
| u32 index_mask; | |
| if (!ref) { | |
| err("Attempt to discard reference 0\n"); | |
| return; | |
| } | |
| if (!tipc_ref_table.entries) { | |
| err("Reference table not found during discard attempt\n"); | |
| return; | |
| } | |
| write_lock_bh(&ref_table_lock); | |
| index_mask = tipc_ref_table.index_mask; | |
| index = ref & index_mask; | |
| entry = &(tipc_ref_table.entries[index]); | |
| if (!entry->object) { | |
| err("Attempt to discard reference to non-existent object\n"); | |
| goto exit; | |
| } | |
| if (entry->data.reference != ref) { | |
| err("Attempt to discard non-existent reference\n"); | |
| goto exit; | |
| } | |
| /* mark entry as unused */ | |
| entry->object = NULL; | |
| if (tipc_ref_table.first_free == 0) | |
| tipc_ref_table.first_free = index; | |
| else | |
| /* next_plus_upper is always XXXX|0--0 for last free entry */ | |
| tipc_ref_table.entries[tipc_ref_table.last_free].data.next_plus_upper | |
| |= index; | |
| tipc_ref_table.last_free = index; | |
| /* increment upper bits of entry to invalidate subsequent references */ | |
| entry->data.next_plus_upper = (ref & ~index_mask) + (index_mask + 1); | |
| exit: | |
| write_unlock_bh(&ref_table_lock); | |
| } | |