/* * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * 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 "ipa_table.h" #include "ipa_nat_utils.h" #include #define IPA_BASE_TABLE_PERCENTAGE .8 #define IPA_EXPANSION_TABLE_PERCENTAGE .2 #define IPA_BASE_TABLE_PCNT_4SRAM 1.00 #define IPA_EXPANSION_TABLE_PCNT_4SRAM 0.43 /* * The table number of entries is limited by Entry ID structure * above. The base table max entries is limited by index into table * bits number. * * The table max ents number is: (base table max ents / base table percentage) * * IPA_TABLE_MAX_ENTRIES = 2^(index into table) / IPA_BASE_TABLE_PERCENTAGE */ static int InsertHead( ipa_table* table, void* rec_ptr, /* empty record in table */ uint16_t rec_index, /* index of record above */ void* user_data, struct ipa_ioc_nat_dma_cmd* cmd ); static int InsertTail( ipa_table* table, void* rec_ptr, /* occupied record at index below */ uint16_t* rec_index_ptr, /* pointer to index of record above */ void* user_data, struct ipa_ioc_nat_dma_cmd* cmd ); static uint16_t MakeEntryHdl( ipa_table* tbl, uint16_t tbl_entry ); static int FindExpnTblFreeEntry( ipa_table* table, void** free_entry, uint16_t* entry_index ); static int Get2PowerTightUpperBound( uint16_t num); static int GetEvenTightUpperBound( uint16_t num); void ipa_table_init( ipa_table* table, const char* table_name, enum ipa3_nat_mem_in nmi, int entry_size, void* meta, int meta_entry_size, ipa_table_entry_interface* entry_interface ) { IPADBG("In\n"); memset(table, 0, sizeof(ipa_table)); strlcpy(table->name, table_name, IPA_RESOURCE_NAME_MAX); table->nmi = nmi; table->entry_size = entry_size; table->meta = meta; table->meta_entry_size = meta_entry_size; table->entry_interface = entry_interface; IPADBG("Table %s with entry size %d has been initialized\n", table->name, table->entry_size); IPADBG("Out\n"); } int ipa_table_calculate_entries_num( ipa_table* table, uint16_t number_of_entries, enum ipa3_nat_mem_in nmi) { uint16_t table_entries, expn_table_entries; float btp, etp; int result = 0; IPADBG("In\n"); if (number_of_entries > IPA_TABLE_MAX_ENTRIES) { IPAERR("Required number of %s entries %d exceeds the maximum %d\n", table->name, number_of_entries, IPA_TABLE_MAX_ENTRIES); result = -EINVAL; goto bail; } if ( nmi == IPA_NAT_MEM_IN_SRAM ) { btp = IPA_BASE_TABLE_PCNT_4SRAM; etp = IPA_EXPANSION_TABLE_PCNT_4SRAM; } else { btp = IPA_BASE_TABLE_PERCENTAGE; etp = IPA_EXPANSION_TABLE_PERCENTAGE; } table_entries = Get2PowerTightUpperBound(number_of_entries * btp); expn_table_entries = GetEvenTightUpperBound(number_of_entries * etp); table->tot_tbl_ents = table_entries + expn_table_entries; if ( table->tot_tbl_ents > IPA_TABLE_MAX_ENTRIES ) { IPAERR("Required number of %s entries %u " "(user provided %u) exceeds the maximum %u\n", table->name, table->tot_tbl_ents, number_of_entries, IPA_TABLE_MAX_ENTRIES); result = -EINVAL; goto bail; } table->table_entries = table_entries; table->expn_table_entries = expn_table_entries; IPADBG("Num of %s entries:%u expn entries:%u total entries:%u\n", table->name, table->table_entries, table->expn_table_entries, table->tot_tbl_ents); bail: IPADBG("Out\n"); return result; } int ipa_table_calculate_size(ipa_table* table) { int size = table->entry_size * (table->table_entries + table->expn_table_entries); IPADBG("In\n"); IPADBG("%s size: %d\n", table->name, size); IPADBG("Out\n"); return size; } uint8_t* ipa_table_calculate_addresses( ipa_table* table, uint8_t* base_addr) { uint8_t* result = NULL; IPADBG("In\n"); table->table_addr = base_addr; table->expn_table_addr = table->table_addr + table->entry_size * table->table_entries; IPADBG("Table %s addresses: table_addr %pK expn_table_addr %pK\n", table->name, table->table_addr, table->expn_table_addr); result = table->expn_table_addr + table->entry_size * table->expn_table_entries; IPADBG("Out\n"); return result; } void ipa_table_reset( ipa_table* table) { uint32_t i,tot; IPADBG("In\n"); IPADBG("memset %s table to 0, %pK\n", table->name, table->table_addr); tot = table->entry_size * table->table_entries; for (i = 0; i < tot; i++) table->table_addr[i] = '\0'; IPADBG("memset %s expn table to 0, %pK\n", table->name, table->expn_table_addr); tot = table->entry_size * table->expn_table_entries; for (i = 0; i < tot; i++) table->expn_table_addr[i] = '\0'; IPADBG("Out\n"); } int ipa_table_add_entry( ipa_table* table, void* user_data, uint16_t* rec_index_ptr, uint32_t* rule_hdl, struct ipa_ioc_nat_dma_cmd* cmd ) { void* rec_ptr; int ret = 0, occupied; IPADBG("In\n"); rec_ptr = GOTO_REC(table, *rec_index_ptr); /* * Check whether there is any collision */ occupied = table->entry_interface->entry_is_valid(rec_ptr); if ( ! occupied ) { IPADBG("Collision free (in %s) ... found open slot\n", table->name); ret = InsertHead(table, rec_ptr, *rec_index_ptr, user_data, cmd); } else { IPADBG("Collision (in %s) ... will probe for open slot\n", table->name); ret = InsertTail(table, rec_ptr, rec_index_ptr, user_data, cmd); } if (ret) goto bail; IPADBG("New Entry Index %u in %s\n", *rec_index_ptr, table->name); if ( rule_hdl ) { *rule_hdl = MakeEntryHdl(table, *rec_index_ptr); IPADBG("rule_hdl value(%u)\n", *rule_hdl); } bail: IPADBG("Out\n"); return ret; } void ipa_table_create_delete_command( ipa_table* table, struct ipa_ioc_nat_dma_cmd* cmd, ipa_table_iterator* iterator) { IPADBG("In\n"); IPADBG("Delete rule at index(0x%04X) in %s\n", iterator->curr_index, table->name); if ( ! VALID_INDEX(iterator->prev_index) ) { /* * The following two assigns (ie. the defaults), will cause * the enabled bit in the record to be set to 0. */ uint16_t data = 0; dma_help_type ht = HELP_UPDATE_HEAD; if ( VALID_INDEX(iterator->next_index) ) { /* * NOTE WELL HERE: * * This record is the first in a chain/list of * records. Delete means something different in this * context. * * The code below will cause the change of the protocol * field in the rule record to 0xFF. It does not set the * enable bit in the record to 0. This is done in special * cases when the record being deleted is the first in a * list of records. * * What does this mean? It means that the record is * functionally deleted, but not really deleted. Why? * Because the IPA will no longer use it because of the * bad protocol (ie. functionally deleted), but these * higher level APIs still see it as "enabled." * * This all means that deleted really means two things: 1) * Not enabled, and 2) Not a valid record. APIs that walk * the table...looking for enabled records (ie. the * enabled bit)....now have to be a bit smarter to see the * bad protocol as well. */ data = table->entry_interface-> entry_get_delete_head_dma_command_data( iterator->curr_entry, iterator->next_entry); ht = HELP_DELETE_HEAD; } ipa_table_add_dma_cmd(table, ht, iterator->curr_entry, iterator->curr_index, data, cmd); } else { ipa_table_add_dma_cmd(table, HELP_UPDATE_ENTRY, iterator->prev_entry, iterator->prev_index, iterator->next_index, cmd); } IPADBG("Out\n"); } void ipa_table_delete_entry( ipa_table* table, ipa_table_iterator* iterator, uint8_t is_prev_empty) { IPADBG("In\n"); if ( VALID_INDEX(iterator->next_index) ) { /* * Update the next entry's prev_index field with current * entry's prev_index */ table->entry_interface->entry_set_prev_index( iterator->next_entry, iterator->next_index, iterator->prev_index, table->meta, table->table_entries); } else if (is_prev_empty) { if (iterator->prev_entry == NULL) { IPAERR("failed to delete of an empty head %d while delete the next entry %d in %s", iterator->prev_index, iterator->curr_index, table->name); } else { /* * Delete an empty head rule after the whole tail was deleted */ IPADBG("deleting the dead node %d for %s\n", iterator->prev_index, table->name); memset(iterator->prev_entry, 0, table->entry_size); --table->cur_tbl_cnt; } } ipa_table_erase_entry(table, iterator->curr_index); IPADBG("Out\n"); } void ipa_table_erase_entry( ipa_table* table, uint16_t index) { void* entry = GOTO_REC(table, index); IPADBG("In\n"); IPADBG("table(%p) index(%u)\n", table, index); memset(entry, 0, table->entry_size); if ( index < table->table_entries ) { --table->cur_tbl_cnt; } else { --table->cur_expn_tbl_cnt; } IPADBG("Out\n"); } /** * ipa_table_get_entry() - returns a table entry according to the received entry handle * @table: [in] the table * @entry_handle: [in] entry handle * @entry: [out] the retrieved entry * @entry_index: [out] absolute index of the retrieved entry * * Parse the entry handle to retrieve the entry and its index * * Returns: 0 on success, negative on failure */ int ipa_table_get_entry( ipa_table* table, uint32_t entry_handle, void** entry, uint16_t* entry_index ) { enum ipa3_nat_mem_in nmi; uint8_t is_expn_tbl; uint16_t rec_index; int ret = 0; IPADBG("In\n"); IPADBG("table(%p) entry_handle(%u) entry(%p) entry_index(%p)\n", table, entry_handle, entry, entry_index); /* * Retrieve the memory and table type as well as the index */ BREAK_RULE_HDL(table, entry_handle, nmi, is_expn_tbl, rec_index); if ( is_expn_tbl ) { IPADBG("Retrieving entry from expansion table\n"); } else { IPADBG("Retrieving entry from base (non-expansion) table\n"); } if ( rec_index >= table->tot_tbl_ents ) { IPAERR("The entry handle's record index (%u) exceeds table size (%u)\n", rec_index, table->tot_tbl_ents); ret = -EINVAL; goto bail; } *entry = GOTO_REC(table, rec_index); if ( entry_index ) { *entry_index = rec_index; } bail: IPADBG("Out\n"); return ret; } void* ipa_table_get_entry_by_index( ipa_table* table, uint16_t rec_index ) { void* result = NULL; IPADBG("In\n"); IPADBG("table(%p) rec_index(%u)\n", table, rec_index); if ( ! rec_index || rec_index >= table->tot_tbl_ents ) { IPAERR("Invalid record index (%u): It's " "either zero or exceeds table size (%u)\n", rec_index, table->tot_tbl_ents); goto bail; } result = GOTO_REC(table, rec_index); bail: IPADBG("Out\n"); return result; } void ipa_table_dma_cmd_helper_init( ipa_table_dma_cmd_helper* dma_cmd_helper, uint8_t table_indx, ipa_table_dma_type table_type, ipa_table_dma_type expn_table_type, uint32_t offset) { IPADBG("In\n"); dma_cmd_helper->offset = offset; dma_cmd_helper->table_indx = table_indx; dma_cmd_helper->table_type = table_type; dma_cmd_helper->expn_table_type = expn_table_type; IPADBG("Out\n"); } void ipa_table_dma_cmd_generate( ipa_table_dma_cmd_helper* dma_cmd_helper, uint8_t is_expn, uint32_t entry_offset, uint16_t data, struct ipa_ioc_nat_dma_cmd* cmd) { struct ipa_ioc_nat_dma_one* dma = &cmd->dma[cmd->entries]; IPADBG("In\n"); IPADBG("is_expn(0x%02X) entry_offset(0x%08X) data(0x%04X)\n", is_expn, entry_offset, data); dma->table_index = dma_cmd_helper->table_indx; /* * DMA parameter base_addr is the table type (see the IPA * architecture document) */ dma->base_addr = (is_expn) ? dma_cmd_helper->expn_table_type : dma_cmd_helper->table_type; dma->offset = dma_cmd_helper->offset + entry_offset; dma->data = data; IPADBG("dma_entry[%u](table_index(0x%02X) " "base_addr(0x%02X) data(0x%04X) offset(0x%08X))\n", cmd->entries, dma->table_index, dma->base_addr, dma->data, dma->offset); cmd->entries++; IPADBG("Out\n"); } int ipa_table_iterator_init( ipa_table_iterator* iterator, ipa_table* table, void* curr_entry, uint16_t curr_index) { int occupied; int ret = 0; IPADBG("In\n"); memset(iterator, 0, sizeof(ipa_table_iterator)); occupied = table->entry_interface->entry_is_valid(curr_entry); if ( ! occupied ) { IPAERR("Invalid (not enabled) rule %u in %s\n", curr_index, table->name); ret = -EINVAL; goto bail; } iterator->curr_entry = curr_entry; iterator->curr_index = curr_index; iterator->prev_index = table->entry_interface->entry_get_prev_index( curr_entry, curr_index, table->meta, table->table_entries); iterator->next_index = table->entry_interface->entry_get_next_index( curr_entry); if ( VALID_INDEX(iterator->prev_index) ) { iterator->prev_entry = ipa_table_get_entry_by_index( table, iterator->prev_index); if ( iterator->prev_entry == NULL ) { IPAERR("Failed to retrieve the entry at index 0x%04X for %s\n", iterator->prev_index, table->name); ret = -EPERM; goto bail; } } if ( VALID_INDEX(iterator->next_index) ) { iterator->next_entry = ipa_table_get_entry_by_index( table, iterator->next_index); if ( iterator->next_entry == NULL ) { IPAERR("Failed to retrieve the entry at index 0x%04X for %s\n", iterator->next_index, table->name); ret = -EPERM; goto bail; } } IPADBG("[index/entry] for " "prev:[0x%04X/%p] " "curr:[0x%04X/%p] " "next:[0x%04X/%p] " "\"%s\"\n", iterator->prev_index, iterator->prev_entry, iterator->curr_index, iterator->curr_entry, iterator->next_index, iterator->next_entry, table->name); bail: IPADBG("Out\n"); return ret; } int ipa_table_iterator_next( ipa_table_iterator* iterator, ipa_table* table) { int ret = 0; IPADBG("In\n"); iterator->prev_entry = iterator->curr_entry; iterator->prev_index = iterator->curr_index; iterator->curr_entry = iterator->next_entry; iterator->curr_index = iterator->next_index; iterator->next_index = table->entry_interface->entry_get_next_index( iterator->curr_entry); if ( ! VALID_INDEX(iterator->next_index) ) { iterator->next_entry = NULL; } else { iterator->next_entry = ipa_table_get_entry_by_index( table, iterator->next_index); if (iterator->next_entry == NULL) { IPAERR("Failed to retrieve the entry at index %d for %s\n", iterator->next_index, table->name); ret = -EPERM; goto bail; } } IPADBG("Iterator moved to: prev_index=%d curr_index=%d next_index=%d\n", iterator->prev_index, iterator->curr_index, iterator->next_index); IPADBG(" prev_entry=%pK curr_entry=%pK next_entry=%pK\n", iterator->prev_entry, iterator->curr_entry, iterator->next_entry); bail: IPADBG("Out\n"); return ret; } int ipa_table_iterator_end( ipa_table_iterator* iterator, ipa_table* table_ptr, uint16_t rec_index, /* a table slot relative to hash */ void* rec_ptr ) /* occupant record at index above */ { bool found_end = false; int ret; IPADBG("In\n"); if ( ! iterator || ! table_ptr || ! rec_ptr ) { IPAERR("Bad arg: iterator(%p) and/or table_ptr (%p) and/or rec_ptr(%p)\n", iterator, table_ptr, rec_ptr); ret = -1; goto bail; } memset(iterator, 0, sizeof(ipa_table_iterator)); iterator->prev_index = rec_index; iterator->prev_entry = rec_ptr; while ( 1 ) { uint16_t next_index = table_ptr->entry_interface->entry_get_next_index(iterator->prev_entry); if ( ! VALID_INDEX(next_index) ) { found_end = true; break; } if ( next_index == iterator->prev_index ) { IPAERR("next_index(%u) and prev_index(%u) shouldn't be equal in %s\n", next_index, iterator->prev_index, table_ptr->name); break; } iterator->prev_index = next_index; iterator->prev_entry = GOTO_REC(table_ptr, next_index); } if ( found_end ) { IPADBG("Iterator found end of list record\n"); ret = 0; } else { IPAERR("Iterator can't find end of list record\n"); ret = -1; } bail: IPADBG("Out\n"); return ret; } int ipa_table_iterator_is_head_with_tail( ipa_table_iterator* iterator) { int ret = 0; IPADBG("In\n"); ret = VALID_INDEX(iterator->next_index) && ! VALID_INDEX(iterator->prev_index); IPADBG("Out\n"); return ret; } static int InsertHead( ipa_table* table, void* rec_ptr, /* empty record in table */ uint16_t rec_index, /* index of record above */ void* user_data, struct ipa_ioc_nat_dma_cmd* cmd ) { uint16_t enable_data = 0; int ret = 0; IPADBG("In\n"); ret = table->entry_interface->entry_head_insert( rec_ptr, user_data, &enable_data); if (ret) { IPAERR("unable to insert a new entry to the head in %s\n", table->name); goto bail; } ipa_table_add_dma_cmd( table, HELP_UPDATE_HEAD, rec_ptr, rec_index, enable_data, cmd); ++table->cur_tbl_cnt; bail: IPADBG("Out\n"); return ret; } static int InsertTail( ipa_table* table, void* rec_ptr, /* occupied record at index below */ uint16_t* rec_index_ptr, /* pointer to index of record above */ void* user_data, struct ipa_ioc_nat_dma_cmd* cmd ) { bool is_index_tbl = (table->meta) ? true : false; ipa_table_iterator iterator; uint16_t enable_data = 0; int ret = 0; IPADBG("In\n"); /* * The most important side effect of the following is to set the * iterator's prev_index and prev_entry...which will be the last * valid entry on the end of the list. */ ret = ipa_table_iterator_end(&iterator, table, *rec_index_ptr, rec_ptr); if ( ret ) { IPAERR("Failed to reach the end of list following rec_index(%u) in %s\n", *rec_index_ptr, table->name); goto bail; } /* * The most important side effect of the following is to set the * iterator's curr_index and curr_entry with the next available * expansion table open slot. */ ret = FindExpnTblFreeEntry(table, &iterator.curr_entry, &iterator.curr_index); if ( ret ) { IPAERR("FindExpnTblFreeEntry of %s failed\n", table->name); goto bail; } /* * Copy data into curr_entry (ie. open slot). */ if ( is_index_tbl ) { ret = table->entry_interface->entry_tail_insert( iterator.curr_entry, user_data); } else { /* * We need enable bit when not index table, hence... */ ret = table->entry_interface->entry_head_insert( iterator.curr_entry, user_data, &enable_data); } if (ret) { IPAERR("Unable to insert a new entry to the tail in %s\n", table->name); goto bail; } /* * Update curr_entry's prev_index field with iterator.prev_index */ table->entry_interface->entry_set_prev_index( iterator.curr_entry, /* set by FindExpnTblFreeEntry above */ iterator.curr_index, /* set by FindExpnTblFreeEntry above */ iterator.prev_index, /* set by ipa_table_iterator_end above */ table->meta, table->table_entries); if ( ! is_index_tbl ) { /* * Generate dma command to have the IPA update the * curr_entry's enable field when not the index table... */ ipa_table_add_dma_cmd( table, HELP_UPDATE_HEAD, iterator.curr_entry, iterator.curr_index, enable_data, cmd); } /* * Generate a dma command to have the IPA update the prev_entry's * next_index with iterator.curr_index. */ ipa_table_add_dma_cmd( table, HELP_UPDATE_ENTRY, iterator.prev_entry, iterator.prev_index, iterator.curr_index, cmd); ++table->cur_expn_tbl_cnt; *rec_index_ptr = iterator.curr_index; bail: IPADBG("Out\n"); return ret; } /** * MakeEntryHdl() - makes an entry handle * @tbl_hdl: [in] tbl - the table * @tbl_entry: [in] tbl_entry - table entry * * Calculate the entry handle which will be returned to client * * Returns: >0 table entry handle */ static uint16_t MakeEntryHdl( ipa_table* tbl, uint16_t tbl_entry ) { uint16_t entry_hdl = 0; IPADBG("In\n"); if (tbl_entry >= tbl->table_entries) { /* * Update the index into table */ entry_hdl = tbl_entry - tbl->table_entries; entry_hdl = (entry_hdl << IPA_TABLE_TYPE_BITS); /* * Update the expansion table type bit */ entry_hdl = (entry_hdl | IPA_TABLE_TYPE_MASK); } else { entry_hdl = tbl_entry; entry_hdl = (entry_hdl << IPA_TABLE_TYPE_BITS); } /* * Set memory type bit. */ entry_hdl = entry_hdl | (tbl->nmi << IPA_TABLE_TYPE_MEM_SHIFT); IPADBG("In: tbl_entry(%u) Out: entry_hdl(%u)\n", tbl_entry, entry_hdl); IPADBG("Out\n"); return entry_hdl; } static int mt_slot( ipa_table* table_ptr, uint32_t rule_hdl, void* record_ptr, uint16_t record_index, void* meta_record_ptr, uint16_t meta_record_index, void* arb_data_ptr ) { IPADBG("%s: Empty expansion slot: (%u) in table of size: (%u)\n", table_ptr->name, record_index, table_ptr->tot_tbl_ents); return record_index; } /* * returns expn table entry absolute index */ static int FindExpnTblFreeEntry( ipa_table* table, void** free_entry, uint16_t* entry_index ) { int ret; IPADBG("In\n"); if ( ! table || ! free_entry || ! entry_index ) { IPAERR("Bad arg: table(%p) and/or " "free_entry(%p) and/or entry_index(%p)\n", table, free_entry, entry_index); ret = -1; goto bail; } *entry_index = 0; *free_entry = NULL; /* * The following will start walk at expansion slots * (ie. just after table->table_entries)... */ ret = ipa_table_walk(table, table->table_entries, WHEN_SLOT_EMPTY, mt_slot, 0); if ( ret > 0 ) { *entry_index = (uint16_t) ret; *free_entry = GOTO_REC(table, *entry_index); IPADBG("%s: entry_index val (%u) free_entry val (%p)\n", table->name, *entry_index, *free_entry); ret = 0; } else { if ( ret < 0 ) { IPAERR("%s: While searching table for emtpy slot\n", table->name); } else { IPADBG("%s: No empty slots (ie. expansion table full): " "BASE (avail/used): (%u/%u) EXPN (avail/used): (%u/%u)\n", table->name, table->table_entries, table->cur_tbl_cnt, table->expn_table_entries, table->cur_expn_tbl_cnt); } ret = -1; } bail: IPADBG("Out\n"); return ret; } /** * Get2PowerTightUpperBound() - Returns the tight upper bound which is a power of 2 * @num: [in] given number * * Returns the tight upper bound for a given number which is power of 2 * * Returns: the tight upper bound which is power of 2 */ static int Get2PowerTightUpperBound(uint16_t num) { uint16_t tmp = num, prev = 0, curr = 2; if (num == 0) return 2; while (tmp != 1) { prev = curr; curr <<= 1; tmp >>= 1; } return (num == prev) ? prev : curr; } /** * GetEvenTightUpperBound() - Returns the tight upper bound which is an even number * @num: [in] given number * * Returns the tight upper bound for a given number which is an even number * * Returns: the tight upper bound which is an even number */ static int GetEvenTightUpperBound(uint16_t num) { if (num == 0) return 2; return (num % 2) ? num + 1 : num; } int ipa_calc_num_sram_table_entries( uint32_t sram_size, uint32_t table1_ent_size, uint32_t table2_ent_size, uint16_t* num_entries_ptr) { ipa_table nat_table; ipa_table index_table; int size = 0; uint16_t tot; IPADBG("In\n"); IPADBG("sram_size(%x or %u)\n", sram_size, sram_size); *num_entries_ptr = 0; tot = 1; while ( 1 ) { IPADBG("Trying %u entries\n", tot); ipa_table_init(&nat_table, "tmp_sram_table1", IPA_NAT_MEM_IN_DDR, table1_ent_size, NULL, 0, NULL); ipa_table_init(&index_table, "tmp_sram_table1", IPA_NAT_MEM_IN_DDR, table2_ent_size, NULL, 0, NULL); nat_table.table_entries = index_table.table_entries = Get2PowerTightUpperBound(tot * IPA_BASE_TABLE_PCNT_4SRAM); nat_table.expn_table_entries = index_table.expn_table_entries = GetEvenTightUpperBound(tot * IPA_EXPANSION_TABLE_PCNT_4SRAM); size = ipa_table_calculate_size(&nat_table); size += ipa_table_calculate_size(&index_table); IPADBG("%u entries consumes size(0x%x or %u)\n", tot, size, size); if ( size > sram_size ) break; *num_entries_ptr = tot; ++tot; } IPADBG("Optimal number of entries: %u\n", *num_entries_ptr); IPADBG("Out\n"); return (*num_entries_ptr) ? 0 : -1; } int ipa_table_walk( ipa_table* ipa_tbl_ptr, uint16_t start_index, When2Callback when2cb, ipa_table_walk_cb walk_cb, void* arb_data_ptr ) { uint16_t i; uint32_t tot; uint8_t* rec_ptr; void* meta_record_ptr; uint16_t meta_record_index; int ret = 0; IPADBG("In\n"); if ( ! ipa_tbl_ptr || ! VALID_WHEN2CALLBACK(when2cb) || ! walk_cb ) { IPAERR("Bad arg: ipa_tbl_ptr(%p) and/or " "when2cb(%u) and/or walk_cb(%p)\n", ipa_tbl_ptr, when2cb, walk_cb); ret = -EINVAL; goto bail; } tot = ipa_tbl_ptr->table_entries + ipa_tbl_ptr->expn_table_entries; if ( start_index >= tot ) { IPAERR("Bad arg: start_index(%u)\n", start_index); ret = -EINVAL; goto bail; } /* * Go through table... */ for ( i = start_index, rec_ptr = GOTO_REC(ipa_tbl_ptr, start_index); i < tot; i++, rec_ptr += ipa_tbl_ptr->entry_size ) { bool call_back; if ( ipa_tbl_ptr->entry_interface->entry_is_valid(rec_ptr) ) { call_back = (when2cb == WHEN_SLOT_FILLED) ? true : false; } else { call_back = (when2cb == WHEN_SLOT_EMPTY) ? true : false; } if ( call_back ) { uint32_t rule_hdl = MakeEntryHdl(ipa_tbl_ptr, i); meta_record_ptr = NULL; meta_record_index = 0; if ( i >= ipa_tbl_ptr->table_entries && ipa_tbl_ptr->meta ) { meta_record_index = i - ipa_tbl_ptr->table_entries; meta_record_ptr = (uint8_t*) ipa_tbl_ptr->meta + (meta_record_index * ipa_tbl_ptr->meta_entry_size); } ret = walk_cb( ipa_tbl_ptr, rule_hdl, rec_ptr, i, meta_record_ptr, meta_record_index, arb_data_ptr); if ( ret != 0 ) { if ( ret < 0 ) { IPAERR("walk_cb returned non-zero (%d)\n", ret); } else { IPADBG("walk_cb returned non-zero (%d)\n", ret); } goto bail; } } } bail: IPADBG("Out\n"); return ret; } int ipa_table_add_dma_cmd( ipa_table* tbl_ptr, dma_help_type help_type, void* rec_ptr, uint16_t rec_index, uint16_t data_for_entry, struct ipa_ioc_nat_dma_cmd* cmd_ptr ) { ipa_table_dma_cmd_helper* help_ptr; uint32_t tab_sz, entry_offset; uint8_t is_expn; int ret = 0; IPADBG("In\n"); if ( ! tbl_ptr || ! VALID_DMA_HELP_TYPE(help_type) || ! rec_ptr || ! cmd_ptr ) { IPAERR("Bad arg: tbl_ptr(%p) and/or help_type(%u) " "and/or rec_ptr(%p) and/or cmd_ptr(%p)\n", tbl_ptr, help_type, rec_ptr, cmd_ptr); ret = -EINVAL; goto bail; } tab_sz = tbl_ptr->table_entries + tbl_ptr->expn_table_entries; if ( rec_index >= tab_sz ) { IPAERR("Bad arg: rec_index(%u)\n", rec_index); ret = -EINVAL; goto bail; } is_expn = (rec_index >= tbl_ptr->table_entries); entry_offset = (uint8_t*) rec_ptr - ((is_expn) ? tbl_ptr->expn_table_addr : tbl_ptr->table_addr); ipa_table_dma_cmd_generate( tbl_ptr->dma_help[help_type], is_expn, entry_offset, data_for_entry, cmd_ptr); bail: IPADBG("Out\n"); return ret; }