Commit a639e7ca8e8282b75be2724a28bfc788aa3bb156

Authored by Paul Moore
Committed by James Morris
1 parent 7b41b1733c

SELinux: Made netnode cache adds faster

When adding new entries to the network node cache we would walk the entire
hash bucket to make sure we didn't cross a threshold (done to bound the
cache size).  This isn't a very quick or elegant solution for something
which is supposed to be quick-ish so add a counter to each hash bucket to
track the size of the bucket and eliminate the need to walk the entire
bucket list on each add.

Signed-off-by: Paul Moore <paul.moore@hp.com>
Signed-off-by: James Morris <jmorris@namei.org>

Showing 1 changed file with 49 additions and 55 deletions Side-by-side Diff

security/selinux/netnode.c
... ... @@ -40,11 +40,17 @@
40 40 #include <net/ipv6.h>
41 41 #include <asm/bug.h>
42 42  
  43 +#include "netnode.h"
43 44 #include "objsec.h"
44 45  
45 46 #define SEL_NETNODE_HASH_SIZE 256
46 47 #define SEL_NETNODE_HASH_BKT_LIMIT 16
47 48  
  49 +struct sel_netnode_bkt {
  50 + unsigned int size;
  51 + struct list_head list;
  52 +};
  53 +
48 54 struct sel_netnode {
49 55 struct netnode_security_struct nsec;
50 56  
... ... @@ -60,7 +66,7 @@
60 66  
61 67 static LIST_HEAD(sel_netnode_list);
62 68 static DEFINE_SPINLOCK(sel_netnode_lock);
63   -static struct list_head sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
  69 +static struct sel_netnode_bkt sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
64 70  
65 71 /**
66 72 * sel_netnode_free - Frees a node entry
... ... @@ -87,7 +93,7 @@
87 93 * the bucket number for the given IP address.
88 94 *
89 95 */
90   -static u32 sel_netnode_hashfn_ipv4(__be32 addr)
  96 +static unsigned int sel_netnode_hashfn_ipv4(__be32 addr)
91 97 {
92 98 /* at some point we should determine if the mismatch in byte order
93 99 * affects the hash function dramatically */
... ... @@ -103,7 +109,7 @@
103 109 * the bucket number for the given IP address.
104 110 *
105 111 */
106   -static u32 sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
  112 +static unsigned int sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
107 113 {
108 114 /* just hash the least significant 32 bits to keep things fast (they
109 115 * are the most likely to be different anyway), we can revisit this
... ... @@ -123,7 +129,7 @@
123 129 */
124 130 static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
125 131 {
126   - u32 idx;
  132 + unsigned int idx;
127 133 struct sel_netnode *node;
128 134  
129 135 switch (family) {
... ... @@ -137,7 +143,7 @@
137 143 BUG();
138 144 }
139 145  
140   - list_for_each_entry_rcu(node, &sel_netnode_hash[idx], list)
  146 + list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list)
141 147 if (node->nsec.family == family)
142 148 switch (family) {
143 149 case PF_INET:
144 150  
145 151  
... ... @@ -159,15 +165,12 @@
159 165 * @node: the new node record
160 166 *
161 167 * Description:
162   - * Add a new node record to the network address hash table. Returns zero on
163   - * success, negative values on failure.
  168 + * Add a new node record to the network address hash table.
164 169 *
165 170 */
166   -static int sel_netnode_insert(struct sel_netnode *node)
  171 +static void sel_netnode_insert(struct sel_netnode *node)
167 172 {
168   - u32 idx;
169   - u32 count = 0;
170   - struct sel_netnode *iter;
  173 + unsigned int idx;
171 174  
172 175 switch (node->nsec.family) {
173 176 case PF_INET:
174 177  
175 178  
176 179  
... ... @@ -179,35 +182,24 @@
179 182 default:
180 183 BUG();
181 184 }
182   - list_add_rcu(&node->list, &sel_netnode_hash[idx]);
183 185  
  186 + INIT_RCU_HEAD(&node->rcu);
  187 +
184 188 /* we need to impose a limit on the growth of the hash table so check
185 189 * this bucket to make sure it is within the specified bounds */
186   - list_for_each_entry(iter, &sel_netnode_hash[idx], list)
187   - if (++count > SEL_NETNODE_HASH_BKT_LIMIT) {
188   - list_del_rcu(&iter->list);
189   - call_rcu(&iter->rcu, sel_netnode_free);
190   - break;
191   - }
192   -
193   - return 0;
  190 + list_add_rcu(&node->list, &sel_netnode_hash[idx].list);
  191 + if (sel_netnode_hash[idx].size == SEL_NETNODE_HASH_BKT_LIMIT) {
  192 + struct sel_netnode *tail;
  193 + tail = list_entry(
  194 + rcu_dereference(sel_netnode_hash[idx].list.prev),
  195 + struct sel_netnode, list);
  196 + list_del_rcu(&tail->list);
  197 + call_rcu(&tail->rcu, sel_netnode_free);
  198 + } else
  199 + sel_netnode_hash[idx].size++;
194 200 }
195 201  
196 202 /**
197   - * sel_netnode_destroy - Remove a node record from the table
198   - * @node: the existing node record
199   - *
200   - * Description:
201   - * Remove an existing node record from the network address table.
202   - *
203   - */
204   -static void sel_netnode_destroy(struct sel_netnode *node)
205   -{
206   - list_del_rcu(&node->list);
207   - call_rcu(&node->rcu, sel_netnode_free);
208   -}
209   -
210   -/**
211 203 * sel_netnode_sid_slow - Lookup the SID of a network address using the policy
212 204 * @addr: the IP address
213 205 * @family: the address family
... ... @@ -222,7 +214,7 @@
222 214 */
223 215 static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
224 216 {
225   - int ret;
  217 + int ret = -ENOMEM;
226 218 struct sel_netnode *node;
227 219 struct sel_netnode *new = NULL;
228 220  
229 221  
230 222  
231 223  
232 224  
... ... @@ -230,25 +222,21 @@
230 222 node = sel_netnode_find(addr, family);
231 223 if (node != NULL) {
232 224 *sid = node->nsec.sid;
233   - ret = 0;
234   - goto out;
  225 + spin_unlock_bh(&sel_netnode_lock);
  226 + return 0;
235 227 }
236 228 new = kzalloc(sizeof(*new), GFP_ATOMIC);
237   - if (new == NULL) {
238   - ret = -ENOMEM;
  229 + if (new == NULL)
239 230 goto out;
240   - }
241 231 switch (family) {
242 232 case PF_INET:
243 233 ret = security_node_sid(PF_INET,
244   - addr, sizeof(struct in_addr),
245   - &new->nsec.sid);
  234 + addr, sizeof(struct in_addr), sid);
246 235 new->nsec.addr.ipv4 = *(__be32 *)addr;
247 236 break;
248 237 case PF_INET6:
249 238 ret = security_node_sid(PF_INET6,
250   - addr, sizeof(struct in6_addr),
251   - &new->nsec.sid);
  239 + addr, sizeof(struct in6_addr), sid);
252 240 ipv6_addr_copy(&new->nsec.addr.ipv6, addr);
253 241 break;
254 242 default:
255 243  
... ... @@ -256,11 +244,10 @@
256 244 }
257 245 if (ret != 0)
258 246 goto out;
  247 +
259 248 new->nsec.family = family;
260   - ret = sel_netnode_insert(new);
261   - if (ret != 0)
262   - goto out;
263   - *sid = new->nsec.sid;
  249 + new->nsec.sid = *sid;
  250 + sel_netnode_insert(new);
264 251  
265 252 out:
266 253 spin_unlock_bh(&sel_netnode_lock);
267 254  
... ... @@ -312,13 +299,18 @@
312 299 */
313 300 static void sel_netnode_flush(void)
314 301 {
315   - u32 idx;
316   - struct sel_netnode *node;
  302 + unsigned int idx;
  303 + struct sel_netnode *node, *node_tmp;
317 304  
318 305 spin_lock_bh(&sel_netnode_lock);
319   - for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++)
320   - list_for_each_entry(node, &sel_netnode_hash[idx], list)
321   - sel_netnode_destroy(node);
  306 + for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) {
  307 + list_for_each_entry_safe(node, node_tmp,
  308 + &sel_netnode_hash[idx].list, list) {
  309 + list_del_rcu(&node->list);
  310 + call_rcu(&node->rcu, sel_netnode_free);
  311 + }
  312 + sel_netnode_hash[idx].size = 0;
  313 + }
322 314 spin_unlock_bh(&sel_netnode_lock);
323 315 }
324 316  
... ... @@ -340,8 +332,10 @@
340 332 if (!selinux_enabled)
341 333 return 0;
342 334  
343   - for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++)
344   - INIT_LIST_HEAD(&sel_netnode_hash[iter]);
  335 + for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) {
  336 + INIT_LIST_HEAD(&sel_netnode_hash[iter].list);
  337 + sel_netnode_hash[iter].size = 0;
  338 + }
345 339  
346 340 ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET,
347 341 SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);