Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tipc: Convert node object array to a hash table

Replaces the dynamically allocated array of pointers to the cluster's
node objects with a static hash table. Hash collisions are resolved
using chaining, with a typical hash chain having only a single node,
to avoid degrading performance during processing of incoming packets.
The conversion to a hash table reduces the memory requirements for
TIPC's node table to approximately the same size it had prior to
the previous commit.

In addition to the hash table itself, TIPC now also maintains a
linked list for the node objects, sorted by ascending network address.
This list allows TIPC to continue sending responses to user space
applications that request node and link information in sorted order.
The list also improves performance when name table update messages are
sent by making it easier to identify the nodes that must be notified.

Signed-off-by: Allan Stephens <allan.stephens@windriver.com>
Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>

authored by

Allan Stephens and committed by
Paul Gortmaker
672d99e1 f831c963

+70 -55
+2 -4
net/tipc/name_distr.c
··· 109 109 { 110 110 struct sk_buff *buf_copy; 111 111 struct tipc_node *n_ptr; 112 - u32 n_num; 113 112 114 - for (n_num = 1; n_num <= tipc_highest_node; n_num++) { 115 - n_ptr = tipc_nodes[n_num]; 116 - if (n_ptr && tipc_node_has_active_links(n_ptr)) { 113 + list_for_each_entry(n_ptr, &tipc_node_list, list) { 114 + if (tipc_node_has_active_links(n_ptr)) { 117 115 buf_copy = skb_copy(buf, GFP_ATOMIC); 118 116 if (!buf_copy) 119 117 break;
+5 -10
net/tipc/net.c
··· 39 39 #include "name_distr.h" 40 40 #include "subscr.h" 41 41 #include "port.h" 42 + #include "node.h" 42 43 #include "config.h" 43 44 44 45 /* ··· 109 108 */ 110 109 111 110 DEFINE_RWLOCK(tipc_net_lock); 112 - struct tipc_node **tipc_nodes; 113 - u32 tipc_highest_node; 114 111 atomic_t tipc_num_links; 115 112 116 113 static int net_start(void) 117 114 { 118 - tipc_nodes = kcalloc(4096, sizeof(*tipc_nodes), GFP_ATOMIC); 119 - tipc_highest_node = 0; 120 115 atomic_set(&tipc_num_links, 0); 121 116 122 - return tipc_nodes ? 0 : -ENOMEM; 117 + return 0; 123 118 } 124 119 125 120 static void net_stop(void) 126 121 { 127 - u32 n_num; 122 + struct tipc_node *node, *t_node; 128 123 129 - for (n_num = 1; n_num <= tipc_highest_node; n_num++) 130 - tipc_node_delete(tipc_nodes[n_num]); 131 - kfree(tipc_nodes); 132 - tipc_nodes = NULL; 124 + list_for_each_entry_safe(node, t_node, &tipc_node_list, list) 125 + tipc_node_delete(node); 133 126 } 134 127 135 128 static void net_route_named_msg(struct sk_buff *buf)
-4
net/tipc/net.h
··· 37 37 #ifndef _TIPC_NET_H 38 38 #define _TIPC_NET_H 39 39 40 - struct tipc_node; 41 - 42 - extern struct tipc_node **tipc_nodes; 43 - extern u32 tipc_highest_node; 44 40 extern atomic_t tipc_num_links; 45 41 46 42 extern rwlock_t tipc_net_lock;
+43 -27
net/tipc/node.c
··· 44 44 45 45 static DEFINE_SPINLOCK(node_create_lock); 46 46 47 + static struct hlist_head node_htable[NODE_HTABLE_SIZE]; 48 + LIST_HEAD(tipc_node_list); 49 + static u32 tipc_num_nodes; 47 50 u32 tipc_own_tag; 51 + 52 + /** 53 + * tipc_node_find - locate specified node object, if it exists 54 + */ 55 + 56 + struct tipc_node *tipc_node_find(u32 addr) 57 + { 58 + struct tipc_node *node; 59 + struct hlist_node *pos; 60 + 61 + if (unlikely(!in_own_cluster(addr))) 62 + return NULL; 63 + 64 + hlist_for_each_entry(node, pos, &node_htable[tipc_hashfn(addr)], hash) { 65 + if (node->addr == addr) 66 + return node; 67 + } 68 + return NULL; 69 + } 48 70 49 71 /** 50 72 * tipc_node_create - create neighboring node ··· 80 58 81 59 struct tipc_node *tipc_node_create(u32 addr) 82 60 { 83 - struct tipc_node *n_ptr; 84 - u32 n_num; 61 + struct tipc_node *n_ptr, *temp_node; 85 62 86 63 spin_lock_bh(&node_create_lock); 87 64 ··· 99 78 100 79 n_ptr->addr = addr; 101 80 spin_lock_init(&n_ptr->lock); 81 + INIT_HLIST_NODE(&n_ptr->hash); 82 + INIT_LIST_HEAD(&n_ptr->list); 102 83 INIT_LIST_HEAD(&n_ptr->nsub); 103 84 104 - n_num = tipc_node(addr); 105 - tipc_nodes[n_num] = n_ptr; 106 - if (n_num > tipc_highest_node) 107 - tipc_highest_node = n_num; 85 + hlist_add_head(&n_ptr->hash, &node_htable[tipc_hashfn(addr)]); 86 + 87 + list_for_each_entry(temp_node, &tipc_node_list, list) { 88 + if (n_ptr->addr < temp_node->addr) 89 + break; 90 + } 91 + list_add_tail(&n_ptr->list, &temp_node->list); 92 + 93 + tipc_num_nodes++; 108 94 109 95 spin_unlock_bh(&node_create_lock); 110 96 return n_ptr; ··· 119 91 120 92 void tipc_node_delete(struct tipc_node *n_ptr) 121 93 { 122 - u32 n_num; 123 - 124 - if (!n_ptr) 125 - return; 126 - 127 - n_num = tipc_node(n_ptr->addr); 128 - tipc_nodes[n_num] = NULL; 94 + list_del(&n_ptr->list); 95 + hlist_del(&n_ptr->hash); 129 96 kfree(n_ptr); 130 97 131 - while (!tipc_nodes[tipc_highest_node]) 132 - if (--tipc_highest_node == 0) 133 - break; 98 + tipc_num_nodes--; 134 99 } 135 100 136 101 ··· 400 379 struct tipc_node *n_ptr; 401 380 struct tipc_node_info node_info; 402 381 u32 payload_size; 403 - u32 n_num; 404 382 405 383 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR)) 406 384 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); ··· 410 390 " (network address)"); 411 391 412 392 read_lock_bh(&tipc_net_lock); 413 - if (!tipc_nodes) { 393 + if (!tipc_num_nodes) { 414 394 read_unlock_bh(&tipc_net_lock); 415 395 return tipc_cfg_reply_none(); 416 396 } 417 397 418 398 /* For now, get space for all other nodes */ 419 399 420 - payload_size = TLV_SPACE(sizeof(node_info)) * 421 - (tipc_highest_node - 1); 400 + payload_size = TLV_SPACE(sizeof(node_info)) * tipc_num_nodes; 422 401 if (payload_size > 32768u) { 423 402 read_unlock_bh(&tipc_net_lock); 424 403 return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED ··· 431 412 432 413 /* Add TLVs for all nodes in scope */ 433 414 434 - for (n_num = 1; n_num <= tipc_highest_node; n_num++) { 435 - n_ptr = tipc_nodes[n_num]; 436 - if (!n_ptr || !tipc_in_scope(domain, n_ptr->addr)) 415 + list_for_each_entry(n_ptr, &tipc_node_list, list) { 416 + if (!tipc_in_scope(domain, n_ptr->addr)) 437 417 continue; 438 418 node_info.addr = htonl(n_ptr->addr); 439 419 node_info.up = htonl(tipc_node_is_up(n_ptr)); ··· 451 433 struct tipc_node *n_ptr; 452 434 struct tipc_link_info link_info; 453 435 u32 payload_size; 454 - u32 n_num; 455 436 456 437 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR)) 457 438 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); ··· 489 472 490 473 /* Add TLVs for any other links in scope */ 491 474 492 - for (n_num = 1; n_num <= tipc_highest_node; n_num++) { 475 + list_for_each_entry(n_ptr, &tipc_node_list, list) { 493 476 u32 i; 494 477 495 - n_ptr = tipc_nodes[n_num]; 496 - if (!n_ptr || !tipc_in_scope(domain, n_ptr->addr)) 478 + if (!tipc_in_scope(domain, n_ptr->addr)) 497 479 continue; 498 480 tipc_node_lock(n_ptr); 499 481 for (i = 0; i < MAX_BEARERS; i++) {
+20 -10
net/tipc/node.h
··· 2 2 * net/tipc/node.h: Include file for TIPC node management routines 3 3 * 4 4 * Copyright (c) 2000-2006, Ericsson AB 5 - * Copyright (c) 2005, Wind River Systems 5 + * Copyright (c) 2005, 2010-2011, Wind River Systems 6 6 * All rights reserved. 7 7 * 8 8 * Redistribution and use in source and binary forms, with or without ··· 46 46 * struct tipc_node - TIPC node structure 47 47 * @addr: network address of node 48 48 * @lock: spinlock governing access to structure 49 - * @next: pointer to next node in sorted list of cluster's nodes 49 + * @hash: links to adjacent nodes in unsorted hash chain 50 + * @list: links to adjacent nodes in sorted list of cluster's nodes 50 51 * @nsub: list of "node down" subscriptions monitoring node 51 52 * @active_links: pointers to active links to node 52 53 * @links: pointers to all links to node ··· 70 69 struct tipc_node { 71 70 u32 addr; 72 71 spinlock_t lock; 73 - struct tipc_node *next; 72 + struct hlist_node hash; 73 + struct list_head list; 74 74 struct list_head nsub; 75 75 struct link *active_links[2]; 76 76 struct link *links[MAX_BEARERS]; ··· 92 90 } bclink; 93 91 }; 94 92 93 + #define NODE_HTABLE_SIZE 512 94 + extern struct list_head tipc_node_list; 95 + 96 + /* 97 + * A trivial power-of-two bitmask technique is used for speed, since this 98 + * operation is done for every incoming TIPC packet. The number of hash table 99 + * entries has been chosen so that no hash chain exceeds 8 nodes and will 100 + * usually be much smaller (typically only a single node). 101 + */ 102 + static inline unsigned int tipc_hashfn(u32 addr) 103 + { 104 + return addr & (NODE_HTABLE_SIZE - 1); 105 + } 106 + 95 107 extern u32 tipc_own_tag; 96 108 109 + struct tipc_node *tipc_node_find(u32 addr); 97 110 struct tipc_node *tipc_node_create(u32 addr); 98 111 void tipc_node_delete(struct tipc_node *n_ptr); 99 112 struct tipc_node *tipc_node_attach_link(struct link *l_ptr); ··· 120 103 int tipc_node_is_up(struct tipc_node *n_ptr); 121 104 struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space); 122 105 struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space); 123 - 124 - static inline struct tipc_node *tipc_node_find(u32 addr) 125 - { 126 - if (likely(in_own_cluster(addr))) 127 - return tipc_nodes[tipc_node(addr)]; 128 - return NULL; 129 - } 130 106 131 107 static inline void tipc_node_lock(struct tipc_node *n_ptr) 132 108 {