change console=tty0 to enable linux framebuffer console
[jz_uboot.git] / cpu / ixp / npe / IxEthDBPortUpdate.c
blobcdf114bfc4311002578ce34d8d18fc00cafe77c7
1 /**
2 * @file IxEthDBDBPortUpdate.c
4 * @brief Implementation of dependency and port update handling
5 *
6 * @par
7 * IXP400 SW Release version 2.0
8 *
9 * -- Copyright Notice --
11 * @par
12 * Copyright 2001-2005, Intel Corporation.
13 * All rights reserved.
15 * @par
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. Neither the name of the Intel Corporation nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * @par
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
30 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
41 * @par
42 * -- End of Copyright Notice --
45 #include "IxEthDB_p.h"
47 /* forward prototype declarations */
48 IX_ETH_DB_PRIVATE MacTreeNode* ixEthDBTreeInsert(MacTreeNode *searchTree, MacDescriptor *descriptor);
49 IX_ETH_DB_PRIVATE void ixEthDBCreateTrees(IxEthDBPortMap updatePorts);
50 IX_ETH_DB_PRIVATE MacTreeNode* ixEthDBTreeRebalance(MacTreeNode *searchTree);
51 IX_ETH_DB_PRIVATE void ixEthDBRebalanceTreeToVine(MacTreeNode *root, UINT32 *size);
52 IX_ETH_DB_PRIVATE void ixEthDBRebalanceVineToTree(MacTreeNode *root, UINT32 size);
53 IX_ETH_DB_PRIVATE void ixEthDBRebalanceCompression(MacTreeNode *root, UINT32 count);
54 IX_ETH_DB_PRIVATE UINT32 ixEthDBRebalanceLog2Floor(UINT32 x);
56 extern HashTable dbHashtable;
58 /**
59 * @brief register types requiring automatic updates
61 * @param typeArray array indexed on record types, each
62 * element indicating whether the record type requires an
63 * automatic update (TRUE) or not (FALSE)
65 * Automatic updates are done for registered record types
66 * upon adding, updating (that is, updating the record portID)
67 * and removing records. Whenever an automatic update is triggered
68 * the appropriate ports will be provided with new database
69 * information.
71 * It is assumed that the typeArray parameter is allocated large
72 * enough to hold all the user defined types. Also, the type
73 * array should be initialized to FALSE as this function only
74 * caters for types which do require automatic updates.
76 * Note that this function should be called by the component
77 * initialization function.
79 * @return number of record types registered for automatic
80 * updates
82 * @internal
84 IX_ETH_DB_PUBLIC
85 UINT32 ixEthDBUpdateTypeRegister(BOOL *typeArray)
87 typeArray[IX_ETH_DB_FILTERING_RECORD] = TRUE;
88 typeArray[IX_ETH_DB_FILTERING_VLAN_RECORD] = TRUE;
90 return 2;
93 /**
94 * @brief computes dependencies and triggers port learning tree updates
96 * @param triggerPorts port map consisting in the ports which triggered the update
98 * This function browses through all the ports and determines how to waterfall the update
99 * event from the trigger ports to all other ports depending on them.
101 * Once the list of ports to be updated is determined this function
102 * calls @ref ixEthDBCreateTrees.
104 * @internal
106 IX_ETH_DB_PUBLIC
107 void ixEthDBUpdatePortLearningTrees(IxEthDBPortMap triggerPorts)
109 IxEthDBPortMap updatePorts;
110 UINT32 portIndex;
112 ixEthDBUpdateLock();
114 SET_EMPTY_DEPENDENCY_MAP(updatePorts);
116 for (portIndex = 0 ; portIndex < IX_ETH_DB_NUMBER_OF_PORTS ; portIndex++)
118 PortInfo *port = &ixEthDBPortInfo[portIndex];
119 BOOL mapsCollide;
121 MAPS_COLLIDE(mapsCollide, triggerPorts, port->dependencyPortMap);
123 if (mapsCollide /* do triggers influence this port? */
124 && !IS_PORT_INCLUDED(portIndex, updatePorts) /* and it's not already in the update list */
125 && port->updateMethod.updateEnabled) /* and we're allowed to update it */
127 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Adding port %d to update set\n", portIndex);
129 JOIN_PORT_TO_MAP(updatePorts, portIndex);
131 else
133 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Didn't add port %d to update set, reasons follow:\n", portIndex);
135 if (!mapsCollide)
137 IX_ETH_DB_UPDATE_TRACE("\tMaps don't collide on port %d\n", portIndex);
140 if (IS_PORT_INCLUDED(portIndex, updatePorts))
142 IX_ETH_DB_UPDATE_TRACE("\tPort %d is already in the update set\n", portIndex);
145 if (!port->updateMethod.updateEnabled)
147 IX_ETH_DB_UPDATE_TRACE("\tPort %d doesn't have updateEnabled set\n", portIndex);
152 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Updating port set\n");
154 ixEthDBCreateTrees(updatePorts);
156 ixEthDBUpdateUnlock();
160 * @brief creates learning trees and calls the port update handlers
162 * @param updatePorts set of ports in need of learning trees
164 * This function determines the optimal method of creating learning
165 * trees using a minimal number of database queries, keeping in mind
166 * that different ports can either use the same learning trees or they
167 * can partially share them. The actual tree building routine is
168 * @ref ixEthDBQuery.
170 * @internal
172 IX_ETH_DB_PRIVATE
173 void ixEthDBCreateTrees(IxEthDBPortMap updatePorts)
175 UINT32 portIndex;
176 BOOL result;
177 BOOL portsLeft = TRUE;
179 while (portsLeft)
181 /* get port with minimal dependency map and NULL search tree */
182 UINT32 minPortIndex = MAX_PORT_SIZE;
183 UINT32 minimalSize = MAX_PORT_SIZE;
185 for (portIndex = 0 ; portIndex < IX_ETH_DB_NUMBER_OF_PORTS ; portIndex++)
187 UINT32 size;
188 PortInfo *port = &ixEthDBPortInfo[portIndex];
190 /* generate trees only for ports that need them */
191 if (!port->updateMethod.searchTreePendingWrite && IS_PORT_INCLUDED(portIndex, updatePorts))
193 GET_MAP_SIZE(port->dependencyPortMap, size);
195 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Dependency map for port %d: size %d\n",
196 portIndex, size);
198 if (size < minimalSize)
200 minPortIndex = portIndex;
201 minimalSize = size;
204 else
206 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Skipped port %d from tree diff (%s)\n", portIndex,
207 port->updateMethod.searchTreePendingWrite ? "pending write access" : "ignored by query");
211 /* if a port was found than minimalSize is not MAX_PORT_SIZE */
212 if (minimalSize != MAX_PORT_SIZE)
214 /* minPortIndex is the port we seek */
215 PortInfo *port = &ixEthDBPortInfo[minPortIndex];
217 IxEthDBPortMap query;
218 MacTreeNode *baseTree;
220 /* now try to find a port with minimal map difference */
221 PortInfo *minimalDiffPort = NULL;
222 UINT32 minimalDiff = MAX_PORT_SIZE;
224 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Minimal size port is %d\n", minPortIndex);
226 for (portIndex = 0 ; portIndex < IX_ETH_DB_NUMBER_OF_PORTS ; portIndex++)
228 PortInfo *diffPort = &ixEthDBPortInfo[portIndex];
229 BOOL mapIsSubset;
231 IS_MAP_SUBSET(mapIsSubset, diffPort->dependencyPortMap, port->dependencyPortMap);
234 if (portIndex != minPortIndex
235 && diffPort->updateMethod.searchTree != NULL
236 && mapIsSubset)
238 /* compute size and pick only minimal size difference */
239 UINT32 diffPortSize;
240 UINT32 sizeDifference;
242 GET_MAP_SIZE(diffPort->dependencyPortMap, diffPortSize);
244 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Checking port %d for differences...\n", portIndex);
246 sizeDifference = minimalSize - diffPortSize;
248 if (sizeDifference < minimalDiff)
250 minimalDiffPort = diffPort;
251 minimalDiff = sizeDifference;
253 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Minimal difference 0x%x was found on port %d\n",
254 minimalDiff, portIndex);
259 /* check if filtering is enabled on this port */
260 if ((port->featureStatus & IX_ETH_DB_FILTERING) != 0)
262 /* if minimalDiff is not MAX_PORT_SIZE minimalDiffPort points to the most similar port */
263 if (minimalDiff != MAX_PORT_SIZE)
265 baseTree = ixEthDBCloneMacTreeNode(minimalDiffPort->updateMethod.searchTree);
266 DIFF_MAPS(query, port->dependencyPortMap , minimalDiffPort->dependencyPortMap);
268 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Found minimal diff, extending tree %d on query\n",
269 minimalDiffPort->portID);
271 else /* .. otherwise no similar port was found, build tree from scratch */
273 baseTree = NULL;
275 COPY_DEPENDENCY_MAP(query, port->dependencyPortMap);
277 IX_ETH_DB_UPDATE_TRACE("DB: (Update) No similar diff, creating tree from query\n");
280 IS_EMPTY_DEPENDENCY_MAP(result, query);
282 if (!result) /* otherwise we don't need anything more on top of the cloned tree */
284 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Adding query tree to port %d\n", minPortIndex);
286 /* build learning tree */
287 port->updateMethod.searchTree = ixEthDBQuery(baseTree, query, IX_ETH_DB_ALL_FILTERING_RECORDS, MAX_ELT_SIZE);
289 else
291 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Query is empty, assuming identical nearest tree\n");
293 port->updateMethod.searchTree = baseTree;
296 else
298 /* filtering is not enabled, will download an empty tree */
299 if (port->updateMethod.searchTree != NULL)
301 ixEthDBFreeMacTreeNode(port->updateMethod.searchTree);
304 port->updateMethod.searchTree = NULL;
307 /* mark tree as valid */
308 port->updateMethod.searchTreePendingWrite = TRUE;
310 else
312 portsLeft = FALSE;
314 IX_ETH_DB_UPDATE_TRACE("DB: (Update) No trees to create this round\n");
318 for (portIndex = 0 ; portIndex < IX_ETH_DB_NUMBER_OF_PORTS ; portIndex++)
320 PortInfo *updatePort = &ixEthDBPortInfo[portIndex];
322 if (updatePort->updateMethod.searchTreePendingWrite)
324 IX_ETH_DB_UPDATE_TRACE("DB: (PortUpdate) Starting procedure to upload new search tree (%snull) into NPE %d\n",
325 updatePort->updateMethod.searchTree != NULL ? "not " : "",
326 portIndex);
328 updatePort->updateMethod.updateHandler(portIndex, IX_ETH_DB_FILTERING_RECORD);
334 * @brief standard NPE update handler
336 * @param portID id of the port to be updated
337 * @param type record type to be pushed during this update
339 * The NPE update handler manages updating the NPE databases
340 * given a certain record type.
342 * @internal
344 IX_ETH_DB_PUBLIC
345 IxEthDBStatus ixEthDBNPEUpdateHandler(IxEthDBPortId portID, IxEthDBRecordType type)
347 UINT32 epDelta, blockCount;
348 IxNpeMhMessage message;
349 UINT32 treeSize = 0;
350 PortInfo *port = &ixEthDBPortInfo[portID];
352 /* size selection and type check */
353 if (type == IX_ETH_DB_FILTERING_RECORD || type == IX_ETH_DB_WIFI_RECORD)
355 treeSize = FULL_ELT_BYTE_SIZE;
357 else if (type == IX_ETH_DB_FIREWALL_RECORD)
359 treeSize = FULL_FW_BYTE_SIZE;
361 else
363 return IX_ETH_DB_INVALID_ARG;
366 /* serialize tree into memory */
367 ixEthDBNPETreeWrite(type, treeSize, port->updateMethod.npeUpdateZone, port->updateMethod.searchTree, &epDelta, &blockCount);
369 /* free internal copy */
370 if (port->updateMethod.searchTree != NULL)
372 ixEthDBFreeMacTreeNode(port->updateMethod.searchTree);
375 /* forget last used search tree */
376 port->updateMethod.searchTree = NULL;
377 port->updateMethod.searchTreePendingWrite = FALSE;
379 /* dependending on the update type we do different things */
380 if (type == IX_ETH_DB_FILTERING_RECORD || type == IX_ETH_DB_WIFI_RECORD)
382 IX_STATUS result;
384 FILL_SETMACADDRESSDATABASE_MSG(message, IX_ETH_DB_PORT_ID_TO_NPE_LOGICAL_ID(portID),
385 epDelta, blockCount,
386 IX_OSAL_MMU_VIRT_TO_PHYS(port->updateMethod.npeUpdateZone));
388 IX_ETHDB_SEND_NPE_MSG(IX_ETH_DB_PORT_ID_TO_NPE(portID), message, result);
390 if (result == IX_SUCCESS)
392 IX_ETH_DB_UPDATE_TRACE("DB: (PortUpdate) Finished downloading NPE tree on port %d\n", portID);
394 else
396 ixEthDBPortInfo[portID].agingEnabled = FALSE;
397 ixEthDBPortInfo[portID].updateMethod.updateEnabled = FALSE;
398 ixEthDBPortInfo[portID].updateMethod.userControlled = TRUE;
400 ERROR_LOG("EthDB: (PortUpdate) disabling aging and updates on port %d (assumed dead)\n", portID);
402 ixEthDBDatabaseClear(portID, IX_ETH_DB_ALL_RECORD_TYPES);
404 return IX_ETH_DB_FAIL;
407 return IX_ETH_DB_SUCCESS;
409 else if (type == IX_ETH_DB_FIREWALL_RECORD)
411 return ixEthDBFirewallUpdate(portID, port->updateMethod.npeUpdateZone, epDelta);
414 return IX_ETH_DB_INVALID_ARG;
418 * @brief queries the database for a set of records to be inserted into a given tree
420 * @param searchTree pointer to a tree where insertions will be performed; can be NULL
421 * @param query set of ports that a database record must match to be inserted into the tree
423 * The query method browses through the database, extracts all the descriptors matching
424 * the given query parameter and inserts them into the given learning tree.
425 * Note that this is an append procedure, the given tree needs not to be empty.
426 * A "descriptor matching the query" is a descriptor whose port id is in the query map.
427 * If the given tree is empty (NULL) a new tree is created and returned.
429 * @return the tree root
431 * @internal
433 IX_ETH_DB_PUBLIC
434 MacTreeNode* ixEthDBQuery(MacTreeNode *searchTree, IxEthDBPortMap query, IxEthDBRecordType recordFilter, UINT32 maxEntries)
436 HashIterator iterator;
437 UINT32 entryCount = 0;
439 /* browse database */
440 BUSY_RETRY(ixEthDBInitHashIterator(&dbHashtable, &iterator));
442 while (IS_ITERATOR_VALID(&iterator))
444 MacDescriptor *descriptor = (MacDescriptor *) iterator.node->data;
446 IX_ETH_DB_UPDATE_TRACE("DB: (PortUpdate) querying [%s]:%d on port map ... ",
447 mac2string(descriptor->macAddress),
448 descriptor->portID);
450 if ((descriptor->type & recordFilter) != 0
451 && IS_PORT_INCLUDED(descriptor->portID, query))
453 MacDescriptor *descriptorClone = ixEthDBCloneMacDescriptor(descriptor);
455 IX_ETH_DB_UPDATE_TRACE("match\n");
457 if (descriptorClone != NULL)
459 /* add descriptor to tree */
460 searchTree = ixEthDBTreeInsert(searchTree, descriptorClone);
462 entryCount++;
465 else
467 IX_ETH_DB_UPDATE_TRACE("no match\n");
470 if (entryCount < maxEntries)
472 /* advance to the next record */
473 BUSY_RETRY(ixEthDBIncrementHashIterator(&dbHashtable, &iterator));
475 else
477 /* the NPE won't accept more entries so we can stop now */
478 ixEthDBReleaseHashIterator(&iterator);
480 IX_ETH_DB_UPDATE_TRACE("DB: (PortUpdate) number of elements reached maximum supported by port\n");
482 break;
486 IX_ETH_DB_UPDATE_TRACE("DB: (PortUpdate) query inserted %d records in the search tree\n", entryCount);
488 return ixEthDBTreeRebalance(searchTree);
492 * @brief inserts a mac descriptor into an tree
494 * @param searchTree tree where the insertion is to be performed (may be NULL)
495 * @param descriptor descriptor to insert into tree
497 * @return the tree root
499 * @internal
501 IX_ETH_DB_PRIVATE
502 MacTreeNode* ixEthDBTreeInsert(MacTreeNode *searchTree, MacDescriptor *descriptor)
504 MacTreeNode *currentNode = searchTree;
505 MacTreeNode *insertLocation = NULL;
506 MacTreeNode *newNode;
507 INT32 insertPosition = RIGHT;
509 if (descriptor == NULL)
511 return searchTree;
514 /* create a new node */
515 newNode = ixEthDBAllocMacTreeNode();
517 if (newNode == NULL)
519 /* out of memory */
520 ERROR_LOG("Warning: ixEthDBAllocMacTreeNode returned NULL in file %s:%d (out of memory?)\n", __FILE__, __LINE__);
522 ixEthDBFreeMacDescriptor(descriptor);
524 return NULL;
527 /* populate node */
528 newNode->descriptor = descriptor;
530 /* an empty initial tree is a special case */
531 if (searchTree == NULL)
533 return newNode;
536 /* get insertion location */
537 while (insertLocation == NULL)
539 MacTreeNode *nextNode;
541 /* compare given key with current node key */
542 insertPosition = ixEthDBAddressCompare(descriptor->macAddress, currentNode->descriptor->macAddress);
544 /* navigate down */
545 if (insertPosition == RIGHT)
547 nextNode = currentNode->right;
549 else if (insertPosition == LEFT)
551 nextNode = currentNode->left;
553 else
555 /* error, duplicate key */
556 ERROR_LOG("Warning: trapped insertion of a duplicate MAC address in an NPE search tree\n");
558 /* this will free the MAC descriptor as well */
559 ixEthDBFreeMacTreeNode(newNode);
561 return searchTree;
564 /* when we can no longer dive through the tree we found the insertion place */
565 if (nextNode != NULL)
567 currentNode = nextNode;
569 else
571 insertLocation = currentNode;
575 /* insert node */
576 if (insertPosition == RIGHT)
578 insertLocation->right = newNode;
580 else
582 insertLocation->left = newNode;
585 return searchTree;
589 * @brief balance a tree
591 * @param searchTree tree to balance
593 * Converts a tree into a balanced tree and returns the root of
594 * the balanced tree. The resulting tree is <i>route balanced</i>
595 * not <i>perfectly balanced</i>. This makes no difference to the
596 * average tree search time which is the same in both cases, O(log2(n)).
598 * @return root of the balanced tree or NULL if there's no memory left
600 * @internal
602 IX_ETH_DB_PRIVATE
603 MacTreeNode* ixEthDBTreeRebalance(MacTreeNode *searchTree)
605 MacTreeNode *pseudoRoot = ixEthDBAllocMacTreeNode();
606 UINT32 size;
608 if (pseudoRoot == NULL)
610 /* out of memory */
611 return NULL;
614 pseudoRoot->right = searchTree;
616 ixEthDBRebalanceTreeToVine(pseudoRoot, &size);
617 ixEthDBRebalanceVineToTree(pseudoRoot, size);
619 searchTree = pseudoRoot->right;
621 /* remove pseudoRoot right branch, otherwise it will free the entire tree */
622 pseudoRoot->right = NULL;
624 ixEthDBFreeMacTreeNode(pseudoRoot);
626 return searchTree;
630 * @brief converts a tree into a vine
632 * @param root root of tree to convert
633 * @param size depth of vine (equal to the number of nodes in the tree)
635 * @internal
637 IX_ETH_DB_PRIVATE
638 void ixEthDBRebalanceTreeToVine(MacTreeNode *root, UINT32 *size)
640 MacTreeNode *vineTail = root;
641 MacTreeNode *remainder = vineTail->right;
642 MacTreeNode *tempPtr;
644 *size = 0;
646 while (remainder != NULL)
648 if (remainder->left == NULL)
650 /* move tail down one */
651 vineTail = remainder;
652 remainder = remainder->right;
653 (*size)++;
655 else
657 /* rotate around remainder */
658 tempPtr = remainder->left;
659 remainder->left = tempPtr->right;
660 tempPtr->right = remainder;
661 remainder = tempPtr;
662 vineTail->right = tempPtr;
668 * @brief converts a vine into a balanced tree
670 * @param root vine to convert
671 * @param size depth of vine
673 * @internal
675 IX_ETH_DB_PRIVATE
676 void ixEthDBRebalanceVineToTree(MacTreeNode *root, UINT32 size)
678 UINT32 leafCount = size + 1 - (1 << ixEthDBRebalanceLog2Floor(size + 1));
680 ixEthDBRebalanceCompression(root, leafCount);
682 size = size - leafCount;
684 while (size > 1)
686 ixEthDBRebalanceCompression(root, size / 2);
688 size /= 2;
693 * @brief compresses a vine/tree stage into a more balanced vine/tree
695 * @param root root of the tree to compress
696 * @param count number of "spine" nodes
698 * @internal
700 IX_ETH_DB_PRIVATE
701 void ixEthDBRebalanceCompression(MacTreeNode *root, UINT32 count)
703 MacTreeNode *scanner = root;
704 MacTreeNode *child;
705 UINT32 local_index;
707 for (local_index = 0 ; local_index < count ; local_index++)
709 child = scanner->right;
710 scanner->right = child->right;
711 scanner = scanner->right;
712 child->right = scanner->left;
713 scanner->left = child;
718 * @brief computes |_log2(x)_| (a.k.a. floor(log2(x)))
720 * @param x number to compute |_log2(x)_| for
722 * @return |_log2(x)_|
724 * @internal
726 IX_ETH_DB_PRIVATE
727 UINT32 ixEthDBRebalanceLog2Floor(UINT32 x)
729 UINT32 log = 0;
730 UINT32 val = 1;
732 while (val < x)
734 log++;
735 val <<= 1;
738 return val == x ? log : log - 1;