/*************************************************************************** * (c)2004-2011 Broadcom Corporation * * This program is the proprietary software of Broadcom Corporation and/or its licensors, * and may only be used, duplicated, modified or distributed pursuant to the terms and * conditions of a separate, written license agreement executed between you and Broadcom * (an "Authorized License"). Except as set forth in an Authorized License, Broadcom grants * no license (express or implied), right to use, or waiver of any kind with respect to the * Software, and Broadcom expressly reserves all rights in and to the Software and all * intellectual property rights therein. IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU * HAVE NO RIGHT TO USE THIS SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY * NOTIFY BROADCOM AND DISCONTINUE ALL USE OF THE SOFTWARE. * * Except as expressly set forth in the Authorized License, * * 1. This program, including its structure, sequence and organization, constitutes the valuable trade * secrets of Broadcom, and you shall use all reasonable efforts to protect the confidentiality thereof, * and to use this information only in connection with your use of Broadcom integrated circuit products. * * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" * AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES, REPRESENTATIONS OR * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO * THE SOFTWARE. BROADCOM SPECIFICALLY DISCLAIMS ANY AND ALL IMPLIED WARRANTIES * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, * LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION * OR CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING OUT OF * USE OR PERFORMANCE OF THE SOFTWARE. * * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM OR ITS * LICENSORS BE LIABLE FOR (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR * EXEMPLARY DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO YOUR * USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM HAS BEEN ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES; OR (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT * ACTUALLY PAID FOR THE SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF * ANY LIMITED REMEDY. * * $brcm_Workfile: b_objdb.c $ * $brcm_Revision: 13 $ * $brcm_Date: 10/5/11 12:39p $ * * Module Description: * * Revision History: * * $brcm_Log: /nexus/base/src/b_objdb.c $ * * 13 10/5/11 12:39p erickson * SW7420-1148: backing out /main/8. insert must be explicit. non-proxied * servers require explicit nexus_register_Xxx and nexus_unregister_Xxx. * * 12 10/4/11 4:39p erickson * SW7420-1992: allow b_objdb_remove to receive NULL client. results in * general release/removal. * * 11 9/29/11 7:03p bandrews * SW7420-2078: objdb now accepts NULL clients for verification * * 10 9/16/11 3:40p erickson * SW7420-2064: clean up unprotected callbacks when client exits, allow * re-registration when client connects * * 9 9/13/11 1:24p erickson * SW7420-1148: change client modes to unprotected/protected/untrusted * * 8 9/2/11 10:01a erickson * SW7420-1148: allow implicit insert on acquire. allows non-thunked * server to work with thunked client. * * 7 7/26/11 1:33p erickson * SW7420-1992: add surfacecmp module and examples * * 6 7/26/11 12:07p erickson * SW7420-1992: callback database must take client id so that callbacks * can be disconnected when a client releases * * 5 6/2/11 8:57a erickson * SW7420-1671: add more #include's * * 4 6/1/11 9:05a erickson * SW7420-1671: add #include * * 3 5/13/11 12:18p erickson * SW7420-1873: remove shutdown attribute * * 2 4/7/11 5:26p erickson * SW7420-1671: correct comment * * 1 4/1/11 9:59a erickson * SW7420-1671: refactor object database for multiprocess stress * ************************************************************/ #include "bstd.h" #include "bkni.h" #include "nexus_base.h" #include "b_objdb.h" /* b_objdb is a standalone piece of code. it is very important that there is no #include into some part of a nexus driver. it should have no dependency other than BDBG and NEXUS_ModuleHandle. */ BDBG_MODULE(b_objdb); BDBG_OBJECT_ID(b_objdb); #define BDBG_MSG_TRACE(X) #define LOCK(P_DB) NEXUS_Module_Lock((P_DB)->module) #define UNLOCK(P_DB) NEXUS_Module_Unlock((P_DB)->module) static struct b_objdb_heap *b_objdb_alloc_heap_locked(unsigned nobjects, struct b_objdb_heap *old_heap) { struct b_objdb_heap *heap; BDBG_ASSERT(nobjects>0); heap = BKNI_Malloc(sizeof(*heap)+(nobjects-1)*sizeof(*heap->data)); if(heap) { heap->count = nobjects; BKNI_Memset(heap->data, 0, nobjects*sizeof(*heap->data)); if(old_heap) { unsigned new,old; /* copy valid data into the new heap, and compact it */ for(old=new=0;oldcount;old++) { if(old_heap->data[old].handle) { heap->data[new] = old_heap->data[old]; new++; if(new>=nobjects) { BDBG_WRN(("nexus_driver_module_p_allocate_heap: (%p) not enough %u entries in new heap", heap, nobjects)); break; } } } BKNI_Free(old_heap); } } else { BDBG_WRN(("nexus_driver_module_p_allocate_heap: can't allocate heap for %u entries", nobjects)); } return heap; } int b_objdb_init(struct b_objdb *db, struct b_objdb_class *class_table) { unsigned class_no; BKNI_Memset(db, 0, sizeof(*db)); BDBG_OBJECT_SET(db, b_objdb); db->class_table = class_table; if (db->class_table) { /* create backlink */ for(class_no=0;db->class_table[class_no].destructor;class_no++) { db->class_table[class_no].db = db; } } return 0; } void b_objdb_uninit(struct b_objdb *db) { BDBG_OBJECT_ASSERT(db, b_objdb); /* NOTE: db->module may be NULL here for uninitialized modules. */ if (db->class_table) { unsigned class_no; for(class_no=0;db->class_table[class_no].destructor;class_no++) { struct b_objdb_class *p_class = &db->class_table[class_no]; if (p_class->objects) { BKNI_Free(p_class->objects); p_class->objects = NULL; } } } BDBG_OBJECT_DESTROY(db, b_objdb); } int b_objdb_insert_locked(struct b_objdb_class *p_class, void *handle, const struct b_objdb_client *client, bool acquiring) { int rc = 0; unsigned count; BDBG_ASSERT(p_class); BDBG_OBJECT_ASSERT(p_class->db, b_objdb); BDBG_MSG(("%s %s:%p client=%p", acquiring?"acquire":"insert", p_class->type_name, handle, client)); if (!client) return BERR_TRACE(NEXUS_INVALID_PARAMETER); for(count=0;;) { struct b_objdb_heap *heap = p_class->objects; unsigned i; if(heap) { count = heap->count; } if(acquiring) { for(i=0;idata[i].handle == handle) { if (heap->data[i].acquired_client) { BDBG_WRN(("object already acquired %s:%p", p_class->type_name, handle)); } else { /* successfully acquired */ heap->data[i].acquired_client = client; } goto done; } } /* for non-proxied server, create requires an explicit nexus_register_Xxx and destroy requires an explicit nexus_unregister_Xxx. this allows a non-proxied server to safely destroy an object that is still acquired. */ BDBG_ERR(("acquiring object that wasn't inserted %s:%p", p_class->type_name, handle)); rc = -1; goto done; } /* on a very first round count==0 */ for(i=0;idata[i].handle == NULL) { heap->data[i].handle = handle; heap->data[i].client = client; heap->data[i].acquired_client = NULL; /* not acquired */ heap->data[i].order = p_class->db->object_count++; goto done; } } /* if not found resize heap */ heap = b_objdb_alloc_heap_locked((count+4), heap); if(!heap) { /* wasn't able to allocate new heap, so we keep the old */ rc = BERR_TRACE(NEXUS_OUT_OF_SYSTEM_MEMORY); goto done; } p_class->objects = heap; } done: return rc; } int b_objdb_remove_locked(struct b_objdb_class *p_class, void *handle, const struct b_objdb_client *client, bool releasing) { struct b_objdb_heap *heap; BDBG_ASSERT(p_class); BDBG_OBJECT_ASSERT(p_class->db, b_objdb); BDBG_MSG(("%s %s:%p client=%#x", releasing?"release":"remove", p_class->type_name, handle, client)); (p_class->db->cancel_callbacks_locked)(p_class->db->cancel_callbacks_context, handle, releasing?(void*)client:NULL); heap = p_class->objects; if(heap) { unsigned i; for(i=0;icount;i++) { if(heap->data[i].handle == handle) { if (releasing) { if (heap->data[i].acquired_client == client || !client) { heap->data[i].acquired_client = 0; if (!heap->data[i].client) { heap->data[i].handle = NULL; } } else { BDBG_ERR(("releasing object that is not acquired %s:%p", p_class->type_name, handle)); return -1; } } else { if (!client || client->verify == b_objdb_verify_none || heap->data[i].client == client) { if (heap->data[i].acquired_client) { BDBG_WRN(("removing object which is still acquired %s:%p", p_class->type_name, handle)); } heap->data[i].handle = NULL; heap->data[i].client = 0; heap->data[i].acquired_client = NULL; } else { BDBG_ERR(("attempting to remove object that is not owned %s:%p", p_class->type_name, handle)); return -1; } } goto done; } } BDBG_ERR(("removing unknown object %s:%p", p_class->type_name, handle)); return -1; } done: return 0; } int b_objdb_verify_locked(struct b_objdb_class *p_class, void *handle, const struct b_objdb_client *client) { struct b_objdb_heap *heap; BDBG_ASSERT(p_class); BDBG_OBJECT_ASSERT(p_class->db, b_objdb); if (client && client->verify == b_objdb_verify_none) { return 0; } BDBG_MSG_TRACE(("verify: %s:%p client=%#x", p_class->type_name, handle, client)); heap = p_class->objects; if (heap) { unsigned i; /* TODO: consider hash table for interfaces with many instances and tight performance requirements (e.g. surfaces) */ for(i=0;icount;i++) { if (heap->data[i].handle == handle) { if (client) { if (client->verify == b_objdb_verify_partial) { /* don't need to check ownership. it exists, so it's good. */ return 0; } /* if you are the owner or have it acquired, you are allowed to use this handle */ if (heap->data[i].client == client || heap->data[i].acquired_client == client) { return 0; } } else { /* no client check. the handle/type exists, so we pass. */ return 0; } } } BDBG_WRN(("unknown object: %s:%p", p_class->type_name, handle)); } return -1; } void b_objdb_clear(struct b_objdb *db) { BDBG_OBJECT_ASSERT(db, b_objdb); LOCK(db); if (db->class_table) { unsigned class_no; for(class_no=0;db->class_table[class_no].destructor;class_no++) { struct b_objdb_class *p_class = &db->class_table[class_no]; if (p_class->objects) { BKNI_Memset(p_class->objects->data, 0, sizeof(struct b_objdb_entry) * p_class->objects->count); } } } UNLOCK(db); } int b_objdb_get_newest_entry(struct b_objdb *db, const struct b_objdb_client *client, struct b_objdb_class **p_class, struct b_objdb_entry **p_entry) { struct b_objdb_entry *newest = NULL; unsigned class_no_newest = 0; int rc = -1; unsigned class_no; BDBG_OBJECT_ASSERT(db, b_objdb); LOCK(db); if (!db->class_table) goto done; for(class_no=0;db->class_table[class_no].destructor;class_no++) { struct b_objdb_class *p_class = &db->class_table[class_no]; struct b_objdb_heap *heap = p_class->objects; struct b_objdb_entry *entry = NULL; unsigned i; if (!heap) continue; for(i=0;icount;i++) { if (!heap->data[i].handle) { continue; } /* must be either owned or acquired */ if (heap->data[i].client != client && heap->data[i].acquired_client != client) { continue; } if (entry==NULL || entry->order < heap->data[i].order ) { /* find entry with maximum order */ entry = &heap->data[i]; } } if(entry && (newest==NULL || newest->order < entry->order)) { /* replace if order is larger */ newest = entry; class_no_newest = class_no; } } if (newest) { *p_entry = newest; *p_class = &db->class_table[class_no_newest]; rc = 0; } done: UNLOCK(db); return rc; } void b_objdb_uninit_entry(struct b_objdb_class *p_class, struct b_objdb_entry *entry, const struct b_objdb_client *client) { BDBG_ASSERT(p_class); BDBG_OBJECT_ASSERT(p_class->db, b_objdb); LOCK(p_class->db); if (entry->acquired_client == client) { /* if it was acquired, it must have a release function registered */ BDBG_MSG(("auto-release: [order %u] %s:%p client=%#x", entry->order, p_class->type_name, entry->handle, client)); (p_class->db->cancel_callbacks_locked)(p_class->db->cancel_callbacks_context, entry->handle, (void*)client); BDBG_ASSERT(p_class->release); p_class->release(entry->handle); entry->acquired_client = NULL; } if (entry->client == client) { BDBG_MSG(("auto-remove: [order %u] %s:%p client=%#x", entry->order, p_class->type_name, entry->handle, client)); (p_class->db->cancel_callbacks_locked)(p_class->db->cancel_callbacks_context, entry->handle, NULL); p_class->destructor(entry->handle); entry->handle = NULL; entry->client = NULL; entry->acquired_client = NULL; } UNLOCK(p_class->db); } void b_objdb_uninit_client(struct b_objdb *db, const struct b_objdb_client *client) { BDBG_OBJECT_ASSERT(db, b_objdb); LOCK(db); (db->cancel_callbacks_locked)(db->cancel_callbacks_context, NULL, (void*)client); UNLOCK(db); }