aboutsummaryrefslogtreecommitdiffstats
path: root/security/selinux/ss/ebitmap.c
diff options
context:
space:
mode:
authorKaiGai Kohei <kaigai@ak.jp.nec.com>2007-09-28 13:20:55 -0400
committerJames Morris <jmorris@namei.org>2007-10-16 18:59:34 -0400
commit9fe79ad1e43d236bbbb8edb3cf634356de714c79 (patch)
tree91149cefa28baf692eb55f88f8c544a33e9126df /security/selinux/ss/ebitmap.c
parent3f12070e27b4a213d62607d2bff139793089a77d (diff)
SELinux: improve performance when AVC misses.
* We add ebitmap_for_each_positive_bit() which enables to walk on any positive bit on the given ebitmap, to improve its performance using common bit-operations defined in linux/bitops.h. In the previous version, this logic was implemented using a combination of ebitmap_for_each_bit() and ebitmap_node_get_bit(), but is was worse in performance aspect. This logic is most frequestly used to compute a new AVC entry, so this patch can improve SELinux performance when AVC misses are happen. * struct ebitmap_node is redefined as an array of "unsigned long", to get suitable for using find_next_bit() which is fasted than iteration of shift and logical operation, and to maximize memory usage allocated from general purpose slab. * Any ebitmap_for_each_bit() are repleced by the new implementation in ss/service.c and ss/mls.c. Some of related implementation are changed, however, there is no incompatibility with the previous version. * The width of any new line are less or equal than 80-chars. The following benchmark shows the effect of this patch, when we access many files which have different security context one after another. The number is more than /selinux/avc/cache_threshold, so any access always causes AVC misses. selinux-2.6 selinux-2.6-ebitmap AVG: 22.763 [s] 8.750 [s] STD: 0.265 0.019 ------------------------------------------ 1st: 22.558 [s] 8.786 [s] 2nd: 22.458 [s] 8.750 [s] 3rd: 22.478 [s] 8.754 [s] 4th: 22.724 [s] 8.745 [s] 5th: 22.918 [s] 8.748 [s] 6th: 22.905 [s] 8.764 [s] 7th: 23.238 [s] 8.726 [s] 8th: 22.822 [s] 8.729 [s] Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> Acked-by: Stephen Smalley <sds@tycho.nsa.gov> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/selinux/ss/ebitmap.c')
-rw-r--r--security/selinux/ss/ebitmap.c281
1 files changed, 158 insertions, 123 deletions
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index ce492a6b38ed..ae44c0c9401d 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -10,6 +10,10 @@
10 * 10 *
11 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 11 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
12 */ 12 */
13/*
14 * Updated: KaiGai Kohei <kaigai@ak.jp.nec.com>
15 * Applied standard bit operations to improve bitmap scanning.
16 */
13 17
14#include <linux/kernel.h> 18#include <linux/kernel.h>
15#include <linux/slab.h> 19#include <linux/slab.h>
@@ -29,7 +33,7 @@ int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2)
29 n2 = e2->node; 33 n2 = e2->node;
30 while (n1 && n2 && 34 while (n1 && n2 &&
31 (n1->startbit == n2->startbit) && 35 (n1->startbit == n2->startbit) &&
32 (n1->map == n2->map)) { 36 !memcmp(n1->maps, n2->maps, EBITMAP_SIZE / 8)) {
33 n1 = n1->next; 37 n1 = n1->next;
34 n2 = n2->next; 38 n2 = n2->next;
35 } 39 }
@@ -54,7 +58,7 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src)
54 return -ENOMEM; 58 return -ENOMEM;
55 } 59 }
56 new->startbit = n->startbit; 60 new->startbit = n->startbit;
57 new->map = n->map; 61 memcpy(new->maps, n->maps, EBITMAP_SIZE / 8);
58 new->next = NULL; 62 new->next = NULL;
59 if (prev) 63 if (prev)
60 prev->next = new; 64 prev->next = new;
@@ -84,13 +88,15 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap,
84{ 88{
85 struct ebitmap_node *e_iter = ebmap->node; 89 struct ebitmap_node *e_iter = ebmap->node;
86 struct netlbl_lsm_secattr_catmap *c_iter; 90 struct netlbl_lsm_secattr_catmap *c_iter;
87 u32 cmap_idx; 91 u32 cmap_idx, cmap_sft;
92 int i;
88 93
89 /* This function is a much simpler because SELinux's MAPTYPE happens 94 /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64,
90 * to be the same as NetLabel's NETLBL_CATMAP_MAPTYPE, if MAPTYPE is 95 * however, it is not always compatible with an array of unsigned long
91 * changed from a u64 this function will most likely need to be changed 96 * in ebitmap_node.
92 * as well. It's not ideal but I think the tradeoff in terms of 97 * In addition, you should pay attention the following implementation
93 * neatness and speed is worth it. */ 98 * assumes unsigned long has a width equal with or less than 64-bit.
99 */
94 100
95 if (e_iter == NULL) { 101 if (e_iter == NULL) {
96 *catmap = NULL; 102 *catmap = NULL;
@@ -104,19 +110,27 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap,
104 c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1); 110 c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1);
105 111
106 while (e_iter != NULL) { 112 while (e_iter != NULL) {
107 if (e_iter->startbit >= 113 for (i = 0; i < EBITMAP_UNIT_NUMS; i++) {
108 (c_iter->startbit + NETLBL_CATMAP_SIZE)) { 114 unsigned int delta, e_startbit, c_endbit;
109 c_iter->next = netlbl_secattr_catmap_alloc(GFP_ATOMIC); 115
110 if (c_iter->next == NULL) 116 e_startbit = e_iter->startbit + i * EBITMAP_UNIT_SIZE;
111 goto netlbl_export_failure; 117 c_endbit = c_iter->startbit + NETLBL_CATMAP_SIZE;
112 c_iter = c_iter->next; 118 if (e_startbit >= c_endbit) {
113 c_iter->startbit = e_iter->startbit & 119 c_iter->next
114 ~(NETLBL_CATMAP_SIZE - 1); 120 = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
121 if (c_iter->next == NULL)
122 goto netlbl_export_failure;
123 c_iter = c_iter->next;
124 c_iter->startbit
125 = e_startbit & ~(NETLBL_CATMAP_SIZE - 1);
126 }
127 delta = e_startbit - c_iter->startbit;
128 cmap_idx = delta / NETLBL_CATMAP_MAPSIZE;
129 cmap_sft = delta % NETLBL_CATMAP_MAPSIZE;
130 c_iter->bitmap[cmap_idx]
131 |= e_iter->maps[cmap_idx] << cmap_sft;
132 e_iter = e_iter->next;
115 } 133 }
116 cmap_idx = (e_iter->startbit - c_iter->startbit) /
117 NETLBL_CATMAP_MAPSIZE;
118 c_iter->bitmap[cmap_idx] = e_iter->map;
119 e_iter = e_iter->next;
120 } 134 }
121 135
122 return 0; 136 return 0;
@@ -128,7 +142,7 @@ netlbl_export_failure:
128 142
129/** 143/**
130 * ebitmap_netlbl_import - Import a NetLabel category bitmap into an ebitmap 144 * ebitmap_netlbl_import - Import a NetLabel category bitmap into an ebitmap
131 * @ebmap: the ebitmap to export 145 * @ebmap: the ebitmap to import
132 * @catmap: the NetLabel category bitmap 146 * @catmap: the NetLabel category bitmap
133 * 147 *
134 * Description: 148 * Description:
@@ -142,36 +156,50 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap,
142 struct ebitmap_node *e_iter = NULL; 156 struct ebitmap_node *e_iter = NULL;
143 struct ebitmap_node *emap_prev = NULL; 157 struct ebitmap_node *emap_prev = NULL;
144 struct netlbl_lsm_secattr_catmap *c_iter = catmap; 158 struct netlbl_lsm_secattr_catmap *c_iter = catmap;
145 u32 c_idx; 159 u32 c_idx, c_pos, e_idx, e_sft;
146 160
147 /* This function is a much simpler because SELinux's MAPTYPE happens 161 /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64,
148 * to be the same as NetLabel's NETLBL_CATMAP_MAPTYPE, if MAPTYPE is 162 * however, it is not always compatible with an array of unsigned long
149 * changed from a u64 this function will most likely need to be changed 163 * in ebitmap_node.
150 * as well. It's not ideal but I think the tradeoff in terms of 164 * In addition, you should pay attention the following implementation
151 * neatness and speed is worth it. */ 165 * assumes unsigned long has a width equal with or less than 64-bit.
166 */
152 167
153 do { 168 do {
154 for (c_idx = 0; c_idx < NETLBL_CATMAP_MAPCNT; c_idx++) { 169 for (c_idx = 0; c_idx < NETLBL_CATMAP_MAPCNT; c_idx++) {
155 if (c_iter->bitmap[c_idx] == 0) 170 unsigned int delta;
171 u64 map = c_iter->bitmap[c_idx];
172
173 if (!map)
156 continue; 174 continue;
157 175
158 e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC); 176 c_pos = c_iter->startbit
159 if (e_iter == NULL) 177 + c_idx * NETLBL_CATMAP_MAPSIZE;
160 goto netlbl_import_failure; 178 if (!e_iter
161 if (emap_prev == NULL) 179 || c_pos >= e_iter->startbit + EBITMAP_SIZE) {
162 ebmap->node = e_iter; 180 e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC);
163 else 181 if (!e_iter)
164 emap_prev->next = e_iter; 182 goto netlbl_import_failure;
165 emap_prev = e_iter; 183 e_iter->startbit
166 184 = c_pos - (c_pos % EBITMAP_SIZE);
167 e_iter->startbit = c_iter->startbit + 185 if (emap_prev == NULL)
168 NETLBL_CATMAP_MAPSIZE * c_idx; 186 ebmap->node = e_iter;
169 e_iter->map = c_iter->bitmap[c_idx]; 187 else
188 emap_prev->next = e_iter;
189 emap_prev = e_iter;
190 }
191 delta = c_pos - e_iter->startbit;
192 e_idx = delta / EBITMAP_UNIT_SIZE;
193 e_sft = delta % EBITMAP_UNIT_SIZE;
194 while (map) {
195 e_iter->maps[e_idx++] |= map & (-1UL);
196 map >>= EBITMAP_UNIT_SIZE;
197 }
170 } 198 }
171 c_iter = c_iter->next; 199 c_iter = c_iter->next;
172 } while (c_iter != NULL); 200 } while (c_iter != NULL);
173 if (e_iter != NULL) 201 if (e_iter != NULL)
174 ebmap->highbit = e_iter->startbit + MAPSIZE; 202 ebmap->highbit = e_iter->startbit + EBITMAP_SIZE;
175 else 203 else
176 ebitmap_destroy(ebmap); 204 ebitmap_destroy(ebmap);
177 205
@@ -186,6 +214,7 @@ netlbl_import_failure:
186int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2) 214int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)
187{ 215{
188 struct ebitmap_node *n1, *n2; 216 struct ebitmap_node *n1, *n2;
217 int i;
189 218
190 if (e1->highbit < e2->highbit) 219 if (e1->highbit < e2->highbit)
191 return 0; 220 return 0;
@@ -197,8 +226,10 @@ int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)
197 n1 = n1->next; 226 n1 = n1->next;
198 continue; 227 continue;
199 } 228 }
200 if ((n1->map & n2->map) != n2->map) 229 for (i = 0; i < EBITMAP_UNIT_NUMS; i++) {
201 return 0; 230 if ((n1->maps[i] & n2->maps[i]) != n2->maps[i])
231 return 0;
232 }
202 233
203 n1 = n1->next; 234 n1 = n1->next;
204 n2 = n2->next; 235 n2 = n2->next;
@@ -219,12 +250,8 @@ int ebitmap_get_bit(struct ebitmap *e, unsigned long bit)
219 250
220 n = e->node; 251 n = e->node;
221 while (n && (n->startbit <= bit)) { 252 while (n && (n->startbit <= bit)) {
222 if ((n->startbit + MAPSIZE) > bit) { 253 if ((n->startbit + EBITMAP_SIZE) > bit)
223 if (n->map & (MAPBIT << (bit - n->startbit))) 254 return ebitmap_node_get_bit(n, bit);
224 return 1;
225 else
226 return 0;
227 }
228 n = n->next; 255 n = n->next;
229 } 256 }
230 257
@@ -238,31 +265,35 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
238 prev = NULL; 265 prev = NULL;
239 n = e->node; 266 n = e->node;
240 while (n && n->startbit <= bit) { 267 while (n && n->startbit <= bit) {
241 if ((n->startbit + MAPSIZE) > bit) { 268 if ((n->startbit + EBITMAP_SIZE) > bit) {
242 if (value) { 269 if (value) {
243 n->map |= (MAPBIT << (bit - n->startbit)); 270 ebitmap_node_set_bit(n, bit);
244 } else { 271 } else {
245 n->map &= ~(MAPBIT << (bit - n->startbit)); 272 unsigned int s;
246 if (!n->map) { 273
247 /* drop this node from the bitmap */ 274 ebitmap_node_clr_bit(n, bit);
248 275
249 if (!n->next) { 276 s = find_first_bit(n->maps, EBITMAP_SIZE);
250 /* 277 if (s < EBITMAP_SIZE)
251 * this was the highest map 278 return 0;
252 * within the bitmap 279
253 */ 280 /* drop this node from the bitmap */
254 if (prev) 281 if (!n->next) {
255 e->highbit = prev->startbit + MAPSIZE; 282 /*
256 else 283 * this was the highest map
257 e->highbit = 0; 284 * within the bitmap
258 } 285 */
259 if (prev) 286 if (prev)
260 prev->next = n->next; 287 e->highbit = prev->startbit
288 + EBITMAP_SIZE;
261 else 289 else
262 e->node = n->next; 290 e->highbit = 0;
263
264 kfree(n);
265 } 291 }
292 if (prev)
293 prev->next = n->next;
294 else
295 e->node = n->next;
296 kfree(n);
266 } 297 }
267 return 0; 298 return 0;
268 } 299 }
@@ -277,12 +308,12 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
277 if (!new) 308 if (!new)
278 return -ENOMEM; 309 return -ENOMEM;
279 310
280 new->startbit = bit & ~(MAPSIZE - 1); 311 new->startbit = bit - (bit % EBITMAP_SIZE);
281 new->map = (MAPBIT << (bit - new->startbit)); 312 ebitmap_node_set_bit(new, bit);
282 313
283 if (!n) 314 if (!n)
284 /* this node will be the highest map within the bitmap */ 315 /* this node will be the highest map within the bitmap */
285 e->highbit = new->startbit + MAPSIZE; 316 e->highbit = new->startbit + EBITMAP_SIZE;
286 317
287 if (prev) { 318 if (prev) {
288 new->next = prev->next; 319 new->next = prev->next;
@@ -316,11 +347,11 @@ void ebitmap_destroy(struct ebitmap *e)
316 347
317int ebitmap_read(struct ebitmap *e, void *fp) 348int ebitmap_read(struct ebitmap *e, void *fp)
318{ 349{
319 int rc; 350 struct ebitmap_node *n = NULL;
320 struct ebitmap_node *n, *l; 351 u32 mapunit, count, startbit, index;
352 u64 map;
321 __le32 buf[3]; 353 __le32 buf[3];
322 u32 mapsize, count, i; 354 int rc, i;
323 __le64 map;
324 355
325 ebitmap_init(e); 356 ebitmap_init(e);
326 357
@@ -328,85 +359,89 @@ int ebitmap_read(struct ebitmap *e, void *fp)
328 if (rc < 0) 359 if (rc < 0)
329 goto out; 360 goto out;
330 361
331 mapsize = le32_to_cpu(buf[0]); 362 mapunit = le32_to_cpu(buf[0]);
332 e->highbit = le32_to_cpu(buf[1]); 363 e->highbit = le32_to_cpu(buf[1]);
333 count = le32_to_cpu(buf[2]); 364 count = le32_to_cpu(buf[2]);
334 365
335 if (mapsize != MAPSIZE) { 366 if (mapunit != sizeof(u64) * 8) {
336 printk(KERN_ERR "security: ebitmap: map size %u does not " 367 printk(KERN_ERR "security: ebitmap: map size %u does not "
337 "match my size %Zd (high bit was %d)\n", mapsize, 368 "match my size %Zd (high bit was %d)\n",
338 MAPSIZE, e->highbit); 369 mapunit, sizeof(u64) * 8, e->highbit);
339 goto bad; 370 goto bad;
340 } 371 }
372
373 /* round up e->highbit */
374 e->highbit += EBITMAP_SIZE - 1;
375 e->highbit -= (e->highbit % EBITMAP_SIZE);
376
341 if (!e->highbit) { 377 if (!e->highbit) {
342 e->node = NULL; 378 e->node = NULL;
343 goto ok; 379 goto ok;
344 } 380 }
345 if (e->highbit & (MAPSIZE - 1)) { 381
346 printk(KERN_ERR "security: ebitmap: high bit (%d) is not a "
347 "multiple of the map size (%Zd)\n", e->highbit, MAPSIZE);
348 goto bad;
349 }
350 l = NULL;
351 for (i = 0; i < count; i++) { 382 for (i = 0; i < count; i++) {
352 rc = next_entry(buf, fp, sizeof(u32)); 383 rc = next_entry(&startbit, fp, sizeof(u32));
353 if (rc < 0) { 384 if (rc < 0) {
354 printk(KERN_ERR "security: ebitmap: truncated map\n"); 385 printk(KERN_ERR "security: ebitmap: truncated map\n");
355 goto bad; 386 goto bad;
356 } 387 }
357 n = kzalloc(sizeof(*n), GFP_KERNEL); 388 startbit = le32_to_cpu(startbit);
358 if (!n) {
359 printk(KERN_ERR "security: ebitmap: out of memory\n");
360 rc = -ENOMEM;
361 goto bad;
362 }
363
364 n->startbit = le32_to_cpu(buf[0]);
365 389
366 if (n->startbit & (MAPSIZE - 1)) { 390 if (startbit & (mapunit - 1)) {
367 printk(KERN_ERR "security: ebitmap start bit (%d) is " 391 printk(KERN_ERR "security: ebitmap start bit (%d) is "
368 "not a multiple of the map size (%Zd)\n", 392 "not a multiple of the map unit size (%Zd)\n",
369 n->startbit, MAPSIZE); 393 startbit, mapunit);
370 goto bad_free; 394 goto bad;
371 } 395 }
372 if (n->startbit > (e->highbit - MAPSIZE)) { 396 if (startbit > e->highbit - mapunit) {
373 printk(KERN_ERR "security: ebitmap start bit (%d) is " 397 printk(KERN_ERR "security: ebitmap start bit (%d) is "
374 "beyond the end of the bitmap (%Zd)\n", 398 "beyond the end of the bitmap (%Zd)\n",
375 n->startbit, (e->highbit - MAPSIZE)); 399 startbit, (e->highbit - mapunit));
376 goto bad_free; 400 goto bad;
401 }
402
403 if (!n || startbit >= n->startbit + EBITMAP_SIZE) {
404 struct ebitmap_node *tmp;
405 tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
406 if (!tmp) {
407 printk(KERN_ERR
408 "security: ebitmap: out of memory\n");
409 rc = -ENOMEM;
410 goto bad;
411 }
412 /* round down */
413 tmp->startbit = startbit - (startbit % EBITMAP_SIZE);
414 if (n) {
415 n->next = tmp;
416 } else {
417 e->node = tmp;
418 }
419 n = tmp;
420 } else if (startbit <= n->startbit) {
421 printk(KERN_ERR "security: ebitmap: start bit %d"
422 " comes after start bit %d\n",
423 startbit, n->startbit);
424 goto bad;
377 } 425 }
426
378 rc = next_entry(&map, fp, sizeof(u64)); 427 rc = next_entry(&map, fp, sizeof(u64));
379 if (rc < 0) { 428 if (rc < 0) {
380 printk(KERN_ERR "security: ebitmap: truncated map\n"); 429 printk(KERN_ERR "security: ebitmap: truncated map\n");
381 goto bad_free; 430 goto bad;
382 } 431 }
383 n->map = le64_to_cpu(map); 432 map = le64_to_cpu(map);
384 433
385 if (!n->map) { 434 index = (startbit - n->startbit) / EBITMAP_UNIT_SIZE;
386 printk(KERN_ERR "security: ebitmap: null map in " 435 while (map) {
387 "ebitmap (startbit %d)\n", n->startbit); 436 n->maps[index] = map & (-1UL);
388 goto bad_free; 437 map = map >> EBITMAP_UNIT_SIZE;
438 index++;
389 } 439 }
390 if (l) {
391 if (n->startbit <= l->startbit) {
392 printk(KERN_ERR "security: ebitmap: start "
393 "bit %d comes after start bit %d\n",
394 n->startbit, l->startbit);
395 goto bad_free;
396 }
397 l->next = n;
398 } else
399 e->node = n;
400
401 l = n;
402 } 440 }
403
404ok: 441ok:
405 rc = 0; 442 rc = 0;
406out: 443out:
407 return rc; 444 return rc;
408bad_free:
409 kfree(n);
410bad: 445bad:
411 if (!rc) 446 if (!rc)
412 rc = -EINVAL; 447 rc = -EINVAL;