Commit a639e7ca8e8282b75be2724a28bfc788aa3bb156
Committed by
James Morris
1 parent
7b41b1733c
Exists in
master
and in
20 other branches
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); |