2 * @file IxEthDBDBPortUpdate.c
4 * @brief Implementation of dependency and port update handling
7 * IXP400 SW Release version 2.0
9 * -- Copyright Notice --
12 * Copyright 2001-2005, Intel Corporation.
13 * All rights reserved.
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
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.
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
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
;
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
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
85 UINT32
ixEthDBUpdateTypeRegister(BOOL
*typeArray
)
87 typeArray
[IX_ETH_DB_FILTERING_RECORD
] = TRUE
;
88 typeArray
[IX_ETH_DB_FILTERING_VLAN_RECORD
] = TRUE
;
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.
107 void ixEthDBUpdatePortLearningTrees(IxEthDBPortMap triggerPorts
)
109 IxEthDBPortMap updatePorts
;
114 SET_EMPTY_DEPENDENCY_MAP(updatePorts
);
116 for (portIndex
= 0 ; portIndex
< IX_ETH_DB_NUMBER_OF_PORTS
; portIndex
++)
118 PortInfo
*port
= &ixEthDBPortInfo
[portIndex
];
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
);
133 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Didn't add port %d to update set, reasons follow:\n", portIndex
);
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
173 void ixEthDBCreateTrees(IxEthDBPortMap updatePorts
)
177 BOOL portsLeft
= TRUE
;
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
++)
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",
198 if (size
< minimalSize
)
200 minPortIndex
= portIndex
;
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
];
231 IS_MAP_SUBSET(mapIsSubset
, diffPort
->dependencyPortMap
, port
->dependencyPortMap
);
234 if (portIndex
!= minPortIndex
235 && diffPort
->updateMethod
.searchTree
!= NULL
238 /* compute size and pick only minimal size difference */
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 */
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
);
291 IX_ETH_DB_UPDATE_TRACE("DB: (Update) Query is empty, assuming identical nearest tree\n");
293 port
->updateMethod
.searchTree
= baseTree
;
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
;
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 " : "",
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.
345 IxEthDBStatus
ixEthDBNPEUpdateHandler(IxEthDBPortId portID
, IxEthDBRecordType type
)
347 UINT32 epDelta
, blockCount
;
348 IxNpeMhMessage message
;
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
;
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
)
384 FILL_SETMACADDRESSDATABASE_MSG(message
, IX_ETH_DB_PORT_ID_TO_NPE_LOGICAL_ID(portID
),
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
);
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
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
),
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
);
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
));
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");
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
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
)
514 /* create a new node */
515 newNode
= ixEthDBAllocMacTreeNode();
520 ERROR_LOG("Warning: ixEthDBAllocMacTreeNode returned NULL in file %s:%d (out of memory?)\n", __FILE__
, __LINE__
);
522 ixEthDBFreeMacDescriptor(descriptor
);
528 newNode
->descriptor
= descriptor
;
530 /* an empty initial tree is a special case */
531 if (searchTree
== NULL
)
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
);
545 if (insertPosition
== RIGHT
)
547 nextNode
= currentNode
->right
;
549 else if (insertPosition
== LEFT
)
551 nextNode
= currentNode
->left
;
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
);
564 /* when we can no longer dive through the tree we found the insertion place */
565 if (nextNode
!= NULL
)
567 currentNode
= nextNode
;
571 insertLocation
= currentNode
;
576 if (insertPosition
== RIGHT
)
578 insertLocation
->right
= newNode
;
582 insertLocation
->left
= newNode
;
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
603 MacTreeNode
* ixEthDBTreeRebalance(MacTreeNode
*searchTree
)
605 MacTreeNode
*pseudoRoot
= ixEthDBAllocMacTreeNode();
608 if (pseudoRoot
== 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
);
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)
638 void ixEthDBRebalanceTreeToVine(MacTreeNode
*root
, UINT32
*size
)
640 MacTreeNode
*vineTail
= root
;
641 MacTreeNode
*remainder
= vineTail
->right
;
642 MacTreeNode
*tempPtr
;
646 while (remainder
!= NULL
)
648 if (remainder
->left
== NULL
)
650 /* move tail down one */
651 vineTail
= remainder
;
652 remainder
= remainder
->right
;
657 /* rotate around remainder */
658 tempPtr
= remainder
->left
;
659 remainder
->left
= tempPtr
->right
;
660 tempPtr
->right
= remainder
;
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
676 void ixEthDBRebalanceVineToTree(MacTreeNode
*root
, UINT32 size
)
678 UINT32 leafCount
= size
+ 1 - (1 << ixEthDBRebalanceLog2Floor(size
+ 1));
680 ixEthDBRebalanceCompression(root
, leafCount
);
682 size
= size
- leafCount
;
686 ixEthDBRebalanceCompression(root
, 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
701 void ixEthDBRebalanceCompression(MacTreeNode
*root
, UINT32 count
)
703 MacTreeNode
*scanner
= root
;
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)_|
727 UINT32
ixEthDBRebalanceLog2Floor(UINT32 x
)
738 return val
== x
? log
: log
- 1;