/* Copyright 1997 Acorn Computers Ltd * * 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. */ /***************************************************/ /* File : RMA.c */ /* */ /* Purpose: The browser needs to claim RMA space */ /* for some of the grottier protocols out */ /* there now and again. There are a */ /* sufficiently large number of claims to */ /* warrant a thin veneer over claim and */ /* release of RMA to keep track of blocks */ /* and make sure leaks don't occur. */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 24-Oct-97: Created. */ /***************************************************/ #include <stdlib.h> #include <stdarg.h> #include <string.h> #include "swis.h" #include "wimp.h" #include "wimplib.h" #include "event.h" #include "svcprint.h" #include "Global.h" #include "Utils.h" #include "RMA.h" /* Structures */ typedef struct rma_array_item { unsigned int flags; /* Currently undefined (0) */ browser_data * allocator; void * rma_block; } rma_array_item; /* Locals */ rma_array_item * rma_array = NULL; int rma_array_elements = 0; /* Static function prototypes */ static _kernel_oserror * rma_add_item (int * new_item); static _kernel_oserror * rma_remove_item (int item); /*************************************************/ /* rma_claim() */ /* */ /* Claims a block of RMA, recording the usage by */ /* adding an item to the rma_array_item array. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* for which the claim is taking */ /* place, or NULL for a misc block - */ /* in that case the block can only */ /* be identified for later freeing */ /* by its address; */ /* */ /* Size of the block to allocate, in */ /* bytes; */ /* */ /* Pointer to a void * which will be */ /* filled in with the address of the */ /* new block. */ /* */ /* Assumes: The void * pointer is not NULL. */ /*************************************************/ _kernel_oserror * rma_claim(browser_data * allocator, int size, void ** rma_block) { _kernel_oserror * e; int new_item; #ifdef TRACE if (tl & (1u<<12)) Printf("rma_claim: Called for allocator %p, block size %d\n",allocator,size); #endif if (rma_block) *rma_block = NULL; else return NULL; /* First add an array item to hold details on the RMA claim */ RetError(rma_add_item(&new_item)); /* The above should return an error if allocation fails, */ /* but 'just in case'... */ if (new_item == -1) return make_no_memory_error(22); /* Fill it in, claiming the RMA space in passing */ rma_array[new_item].flags = 0; rma_array[new_item].allocator = allocator; #ifdef TRACE if (tl & (1u<<12)) Printf("rma_claim: \0211Allocating %d bytes RMA\0217\n",size); #endif e = _swix(OS_Module, _IN(0) | _IN(3) | _OUT(2), 6, size, &rma_array[new_item].rma_block); #ifdef TRACE if (tl & (1u<<12)) Printf("rma_claim: \0211Been given block %p\0217\n",rma_array[new_item].rma_block); #endif /* If the claim appears to have failed, free the array item */ /* again and return a generic out of memory error. */ if (!rma_array[new_item].rma_block) { rma_remove_item(new_item); return make_no_memory_error(22); } /* Otherwise, we've finished */ if (rma_block) *rma_block = rma_array[new_item].rma_block; #ifdef TRACE if (tl & (1u<<12)) Printf("rma_claim: Successful\n"); #endif return NULL; } /*************************************************/ /* rma_release() */ /* */ /* Releases (frees) one or several blocks of RMA */ /* claimed by rma_claim, identified by block */ /* address or an owning browser. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* for which the claim originally */ /* took place to free all blocks */ /* claimed for that browser - this */ /* is ignored if the second */ /* parameter is non-NULL (see */ /* below); */ /* */ /* Address of the block - this is */ /* always used in preference to the */ /* browser pointer, so it *must* be */ /* NULL if all blocks for a given */ /* browser are to be freed. */ /*************************************************/ _kernel_oserror * rma_release(browser_data * allocator, void * rma_block) { int search, found, found_any = 0; #ifdef TRACE if (tl & (1u<<12)) Printf("rma_release: Called for allocator %p, block %p\n",allocator,rma_block); #endif /* Sanity check */ if (!allocator && !rma_block) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; StrNCpy0(erb.errmess, "Null allocator and RMA block pointer given to rma_release"); return &erb; #endif return NULL; } /* If we've been given a specific block, search by that. Otherwise, */ /* remove all items owned by the given browser. */ if (rma_block) { do { found = 0; search = 0; while (search < rma_array_elements) { if (rma_array[search].rma_block == rma_block) { /* If an allocator was given, and the block doesn't */ /* match it, then warn about this in TRACE builds. */ #ifdef TRACE if (allocator && rma_array[search].allocator != allocator) { erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Going to free block %p for browser %p, though the block's allocator was %p, in rma_release", rma_block, allocator, rma_array[search].allocator); show_error_ret(&erb); } #endif /* Now free the block */ rma_remove_item(search); found = found_any = 1; break; } search++; } } while (found); } else { do { found = 0; search = 0; while (search < rma_array_elements) { if (rma_array[search].allocator == allocator) { rma_remove_item(search); found = found_any = 1; break; } search++; } } while (found); } /* Trace builds will complain if no item was found */ /* - found_any isn't used for non-TRACE builds at */ /* the moment but could be in future, and so is */ /* unconditionally compiled in. */ #ifdef TRACE if (!found_any) { erb.errnum = Utils_Error_Custom_Message; sprintf(erb.errmess, "Didn't find the item for allocator %p, address %p in rma_release", allocator, rma_block); show_error_ret(&erb); } #endif #ifdef TRACE if (tl & (1u<<12)) Printf("rma_release: Successful\n"); #endif return NULL; } /*************************************************/ /* rma_shutdown() */ /* */ /* Frees all items in array of claimed blocks, */ /* and all associated RMA blocks. */ /* */ /* TRACE builds will complain if there are any */ /* items to free, as usually other processes */ /* should have tidied up and this function */ /* will have nothing to do except exit. */ /*************************************************/ void rma_shutdown(void) { #ifdef TRACE if (tl & (1u<<12)) Printf("rma_shutdown: Called\n"); #endif if (rma_array_elements) { int item; #ifdef TRACE if (tl & (1u<<12)) Printf("rma_shutdown: Have item(s) to free\n"); erb.errnum = Utils_Error_Custom_Message; if (rma_array_elements == 1) { StrNCpy0(erb.errmess, "Possible RMA leak; rma_shutdown has a block to free"); } else { sprintf(erb.errmess, "Possible RMA leak; rma_shutdown has %d blocks to free", rma_array_elements); } show_error_ret(&erb); #endif /* Free all of the RMA blocks */ for (item = 0; item < rma_array_elements; item++) { if (rma_array[item].rma_block) { _swix(OS_Module, _IN(0) | _IN(2), 7, rma_array[item].rma_block); } } /* Free the array itself */ free(rma_array); rma_array = NULL; rma_array_elements = 0; } #ifdef TRACE if (tl & (1u<<12)) Printf("rma_shutdown: Successful\n"); #endif return; } /*************************************************/ /* rma_add_item() */ /* */ /* Adds an rma_array_item struct to the array of */ /* such structures. The contents are not */ /* initialised. */ /* */ /* Parameters: Pointer to an int, in which the */ /* array index of the new item is */ /* placed, or -1 for an error - this */ /* will be in addition to an error */ /* returned directly. */ /* */ /* Assumes: The pointer may not be NULL. */ /*************************************************/ static _kernel_oserror * rma_add_item(int * new_item) { rma_array_item * new_array; #ifdef TRACE if (tl & (1u<<12)) Printf("rma_add_item: Called\n"); #endif if (!new_item) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; StrNCpy0(erb.errmess, "Null new_item int pointer passed to rma_add_item"); return &erb; #endif return NULL; } if (!rma_array_elements) new_array = malloc (sizeof(rma_array_item)); else new_array = realloc(rma_array, sizeof(rma_array_item) * (rma_array_elements + 1)); if (!new_array) return make_no_memory_error(22); else rma_array = new_array; *new_item = rma_array_elements++; /* (Post-increment as we want to return an array index, not how many elements there are) */ #ifdef TRACE if (tl & (1u<<12)) Printf("rma_add_item: Successful, item count is now %d\n", rma_array_elements); #endif return NULL; } /*************************************************/ /* rma_remove_item() */ /* */ /* Removes an rma_array_item from the array of */ /* such structures. */ /* */ /* Parameters: Index into the array of the item */ /* to remove. */ /*************************************************/ static _kernel_oserror * rma_remove_item(int item) { rma_array_item * new_array; #ifdef TRACE if (tl & (1u<<12)) Printf("rma_remove_item: Called for item %d\n",item); #endif /* Limit check 'item' */ if (item >= rma_array_elements) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Invalid item number (%d, with %d elements) passed to rma_remove_item", item, rma_array_elements); return &erb; #endif return NULL; } /* Is there an associated RMA block to free? If so, */ /* free it! */ #ifdef TRACE if (tl & (1u<<12)) Printf("rma_remove_item: \0212Freeing RMA block %p\0217\n",rma_array[item].rma_block); #endif if (rma_array[item].rma_block) { RetError(_swix(OS_Module, _IN(0) | _IN(2), 7, rma_array[item].rma_block)); rma_array[item].rma_block = NULL; } /* Are we removing the only item? If so, just free the array */ if (rma_array_elements == 1) { #ifdef TRACE if (tl & (1u<<12)) Printf("rma_remove_item: This is the last item, so freeing the array\n"); #endif free(rma_array); rma_array = NULL; rma_array_elements = 0; } else { /* Are we removing the last item? If not, must shuffle */ /* the other elements down a bit. */ if (item != rma_array_elements - 1) { #ifdef TRACE if (tl & (1u<<12)) Printf("rma_remove_item: This is not the head item, so shuffling higher items down\n"); #endif memmove(&rma_array[item], &rma_array[item + 1], sizeof(rma_array_item) * (rma_array_elements - (item + 1))); } /* Now shrink the array */ new_array = realloc(rma_array, sizeof(rma_array_item) * (rma_array_elements - 1)); if (new_array) rma_array = new_array, rma_array_elements--; } /* Finished. */ #ifdef TRACE if (tl & (1u<<12)) Printf("rma_remove_item: Successful, item count is now %d\n", rma_array_elements); #endif return NULL; }