Commit 9fe79ad1 authored by KaiGai Kohei's avatar KaiGai Kohei Committed by James Morris

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: default avatarKaiGai Kohei <kaigai@ak.jp.nec.com>
Acked-by: default avatarStephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent 3f12070e
This diff is collapsed.
...@@ -16,14 +16,16 @@ ...@@ -16,14 +16,16 @@
#include <net/netlabel.h> #include <net/netlabel.h>
#define MAPTYPE u64 /* portion of bitmap in each node */ #define EBITMAP_UNIT_NUMS ((32 - sizeof(void *) - sizeof(u32)) \
#define MAPSIZE (sizeof(MAPTYPE) * 8) /* number of bits in node bitmap */ / sizeof(unsigned long))
#define MAPBIT 1ULL /* a bit in the node bitmap */ #define EBITMAP_UNIT_SIZE BITS_PER_LONG
#define EBITMAP_SIZE (EBITMAP_UNIT_NUMS * EBITMAP_UNIT_SIZE)
#define EBITMAP_BIT 1ULL
struct ebitmap_node { struct ebitmap_node {
u32 startbit; /* starting position in the total bitmap */
MAPTYPE map; /* this node's portion of the bitmap */
struct ebitmap_node *next; struct ebitmap_node *next;
unsigned long maps[EBITMAP_UNIT_NUMS];
u32 startbit;
}; };
struct ebitmap { struct ebitmap {
...@@ -34,11 +36,17 @@ struct ebitmap { ...@@ -34,11 +36,17 @@ struct ebitmap {
#define ebitmap_length(e) ((e)->highbit) #define ebitmap_length(e) ((e)->highbit)
#define ebitmap_startbit(e) ((e)->node ? (e)->node->startbit : 0) #define ebitmap_startbit(e) ((e)->node ? (e)->node->startbit : 0)
static inline unsigned int ebitmap_start(struct ebitmap *e, static inline unsigned int ebitmap_start_positive(struct ebitmap *e,
struct ebitmap_node **n) struct ebitmap_node **n)
{ {
*n = e->node; unsigned int ofs;
return ebitmap_startbit(e);
for (*n = e->node; *n; *n = (*n)->next) {
ofs = find_first_bit((*n)->maps, EBITMAP_SIZE);
if (ofs < EBITMAP_SIZE)
return (*n)->startbit + ofs;
}
return ebitmap_length(e);
} }
static inline void ebitmap_init(struct ebitmap *e) static inline void ebitmap_init(struct ebitmap *e)
...@@ -46,28 +54,65 @@ static inline void ebitmap_init(struct ebitmap *e) ...@@ -46,28 +54,65 @@ static inline void ebitmap_init(struct ebitmap *e)
memset(e, 0, sizeof(*e)); memset(e, 0, sizeof(*e));
} }
static inline unsigned int ebitmap_next(struct ebitmap_node **n, static inline unsigned int ebitmap_next_positive(struct ebitmap *e,
unsigned int bit) struct ebitmap_node **n,
unsigned int bit)
{ {
if ((bit == ((*n)->startbit + MAPSIZE - 1)) && unsigned int ofs;
(*n)->next) {
*n = (*n)->next; ofs = find_next_bit((*n)->maps, EBITMAP_SIZE, bit - (*n)->startbit + 1);
return (*n)->startbit; if (ofs < EBITMAP_SIZE)
} return ofs + (*n)->startbit;
return (bit+1); for (*n = (*n)->next; *n; *n = (*n)->next) {
ofs = find_first_bit((*n)->maps, EBITMAP_SIZE);
if (ofs < EBITMAP_SIZE)
return ofs + (*n)->startbit;
}
return ebitmap_length(e);
} }
static inline int ebitmap_node_get_bit(struct ebitmap_node * n, #define EBITMAP_NODE_INDEX(node, bit) \
(((bit) - (node)->startbit) / EBITMAP_UNIT_SIZE)
#define EBITMAP_NODE_OFFSET(node, bit) \
(((bit) - (node)->startbit) % EBITMAP_UNIT_SIZE)
static inline int ebitmap_node_get_bit(struct ebitmap_node *n,
unsigned int bit) unsigned int bit)
{ {
if (n->map & (MAPBIT << (bit - n->startbit))) unsigned int index = EBITMAP_NODE_INDEX(n, bit);
unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit);
BUG_ON(index >= EBITMAP_UNIT_NUMS);
if ((n->maps[index] & (EBITMAP_BIT << ofs)))
return 1; return 1;
return 0; return 0;
} }
#define ebitmap_for_each_bit(e, n, bit) \ static inline void ebitmap_node_set_bit(struct ebitmap_node *n,
for (bit = ebitmap_start(e, &n); bit < ebitmap_length(e); bit = ebitmap_next(&n, bit)) \ unsigned int bit)
{
unsigned int index = EBITMAP_NODE_INDEX(n, bit);
unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit);
BUG_ON(index >= EBITMAP_UNIT_NUMS);
n->maps[index] |= (EBITMAP_BIT << ofs);
}
static inline void ebitmap_node_clr_bit(struct ebitmap_node *n,
unsigned int bit)
{
unsigned int index = EBITMAP_NODE_INDEX(n, bit);
unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit);
BUG_ON(index >= EBITMAP_UNIT_NUMS);
n->maps[index] &= ~(EBITMAP_BIT << ofs);
}
#define ebitmap_for_each_positive_bit(e, n, bit) \
for (bit = ebitmap_start_positive(e, &n); \
bit < ebitmap_length(e); \
bit = ebitmap_next_positive(e, &n, bit)) \
int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2); int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2);
int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src); int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src);
......
...@@ -34,7 +34,9 @@ ...@@ -34,7 +34,9 @@
*/ */
int mls_compute_context_len(struct context * context) int mls_compute_context_len(struct context * context)
{ {
int i, l, len, range; int i, l, len, head, prev;
char *nm;
struct ebitmap *e;
struct ebitmap_node *node; struct ebitmap_node *node;
if (!selinux_mls_enabled) if (!selinux_mls_enabled)
...@@ -42,31 +44,33 @@ int mls_compute_context_len(struct context * context) ...@@ -42,31 +44,33 @@ int mls_compute_context_len(struct context * context)
len = 1; /* for the beginning ":" */ len = 1; /* for the beginning ":" */
for (l = 0; l < 2; l++) { for (l = 0; l < 2; l++) {
range = 0; int index_sens = context->range.level[l].sens;
len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); len += strlen(policydb.p_sens_val_to_name[index_sens - 1]);
ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
if (ebitmap_node_get_bit(node, i)) {
if (range) {
range++;
continue;
}
len += strlen(policydb.p_cat_val_to_name[i]) + 1; /* categories */
range++; head = -2;
} else { prev = -2;
if (range > 1) e = &context->range.level[l].cat;
len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1; ebitmap_for_each_positive_bit(e, node, i) {
range = 0; if (i - prev > 1) {
/* one or more negative bits are skipped */
if (head != prev) {
nm = policydb.p_cat_val_to_name[prev];
len += strlen(nm) + 1;
}
nm = policydb.p_cat_val_to_name[i];
len += strlen(nm) + 1;
head = i;
} }
prev = i;
}
if (prev != head) {
nm = policydb.p_cat_val_to_name[prev];
len += strlen(nm) + 1;
} }
/* Handle case where last category is the end of range */
if (range > 1)
len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
if (l == 0) { if (l == 0) {
if (mls_level_eq(&context->range.level[0], if (mls_level_eq(&context->range.level[0],
&context->range.level[1])) &context->range.level[1]))
break; break;
else else
len++; len++;
...@@ -84,8 +88,9 @@ int mls_compute_context_len(struct context * context) ...@@ -84,8 +88,9 @@ int mls_compute_context_len(struct context * context)
void mls_sid_to_context(struct context *context, void mls_sid_to_context(struct context *context,
char **scontext) char **scontext)
{ {
char *scontextp; char *scontextp, *nm;
int i, l, range, wrote_sep; int i, l, head, prev;
struct ebitmap *e;
struct ebitmap_node *node; struct ebitmap_node *node;
if (!selinux_mls_enabled) if (!selinux_mls_enabled)
...@@ -97,61 +102,54 @@ void mls_sid_to_context(struct context *context, ...@@ -97,61 +102,54 @@ void mls_sid_to_context(struct context *context,
scontextp++; scontextp++;
for (l = 0; l < 2; l++) { for (l = 0; l < 2; l++) {
range = 0;
wrote_sep = 0;
strcpy(scontextp, strcpy(scontextp,
policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); scontextp += strlen(scontextp);
/* categories */ /* categories */
ebitmap_for_each_bit(&context->range.level[l].cat, node, i) { head = -2;
if (ebitmap_node_get_bit(node, i)) { prev = -2;
if (range) { e = &context->range.level[l].cat;
range++; ebitmap_for_each_positive_bit(e, node, i) {
continue; if (i - prev > 1) {
} /* one or more negative bits are skipped */
if (prev != head) {
if (!wrote_sep) { if (prev - head > 1)
*scontextp++ = ':';
wrote_sep = 1;
} else
*scontextp++ = ',';
strcpy(scontextp, policydb.p_cat_val_to_name[i]);
scontextp += strlen(policydb.p_cat_val_to_name[i]);
range++;
} else {
if (range > 1) {
if (range > 2)
*scontextp++ = '.'; *scontextp++ = '.';
else else
*scontextp++ = ','; *scontextp++ = ',';
nm = policydb.p_cat_val_to_name[prev];
strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]); strcpy(scontextp, nm);
scontextp += strlen(policydb.p_cat_val_to_name[i - 1]); scontextp += strlen(nm);
} }
range = 0; if (prev < 0)
*scontextp++ = ':';
else
*scontextp++ = ',';
nm = policydb.p_cat_val_to_name[i];
strcpy(scontextp, nm);
scontextp += strlen(nm);
head = i;
} }
prev = i;
} }
/* Handle case where last category is the end of range */ if (prev != head) {
if (range > 1) { if (prev - head > 1)
if (range > 2)
*scontextp++ = '.'; *scontextp++ = '.';
else else
*scontextp++ = ','; *scontextp++ = ',';
nm = policydb.p_cat_val_to_name[prev];
strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]); strcpy(scontextp, nm);
scontextp += strlen(policydb.p_cat_val_to_name[i - 1]); scontextp += strlen(nm);
} }
if (l == 0) { if (l == 0) {
if (mls_level_eq(&context->range.level[0], if (mls_level_eq(&context->range.level[0],
&context->range.level[1])) &context->range.level[1]))
break; break;
else { else
*scontextp = '-'; *scontextp++ = '-';
scontextp++;
}
} }
} }
...@@ -190,17 +188,15 @@ int mls_context_isvalid(struct policydb *p, struct context *c) ...@@ -190,17 +188,15 @@ int mls_context_isvalid(struct policydb *p, struct context *c)
if (!levdatum) if (!levdatum)
return 0; return 0;
ebitmap_for_each_bit(&c->range.level[l].cat, node, i) { ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) {
if (ebitmap_node_get_bit(node, i)) { if (i > p->p_cats.nprim)
if (i > p->p_cats.nprim) return 0;
return 0; if (!ebitmap_get_bit(&levdatum->level->cat, i))
if (!ebitmap_get_bit(&levdatum->level->cat, i)) /*
/* * Category may not be associated with
* Category may not be associated with * sensitivity in low level.
* sensitivity in low level. */
*/ return 0;
return 0;
}
} }
} }
...@@ -485,18 +481,16 @@ int mls_convert_context(struct policydb *oldp, ...@@ -485,18 +481,16 @@ int mls_convert_context(struct policydb *oldp,
c->range.level[l].sens = levdatum->level->sens; c->range.level[l].sens = levdatum->level->sens;
ebitmap_init(&bitmap); ebitmap_init(&bitmap);
ebitmap_for_each_bit(&c->range.level[l].cat, node, i) { ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) {
if (ebitmap_node_get_bit(node, i)) { int rc;
int rc;
catdatum = hashtab_search(newp->p_cats.table,
catdatum = hashtab_search(newp->p_cats.table, oldp->p_cat_val_to_name[i]);
oldp->p_cat_val_to_name[i]); if (!catdatum)
if (!catdatum) return -EINVAL;
return -EINVAL; rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1); if (rc)
if (rc) return rc;
return rc;
}
} }
ebitmap_destroy(&c->range.level[l].cat); ebitmap_destroy(&c->range.level[l].cat);
c->range.level[l].cat = bitmap; c->range.level[l].cat = bitmap;
......
...@@ -353,12 +353,8 @@ static int context_struct_compute_av(struct context *scontext, ...@@ -353,12 +353,8 @@ static int context_struct_compute_av(struct context *scontext,
avkey.specified = AVTAB_AV; avkey.specified = AVTAB_AV;
sattr = &policydb.type_attr_map[scontext->type - 1]; sattr = &policydb.type_attr_map[scontext->type - 1];
tattr = &policydb.type_attr_map[tcontext->type - 1]; tattr = &policydb.type_attr_map[tcontext->type - 1];
ebitmap_for_each_bit(sattr, snode, i) { ebitmap_for_each_positive_bit(sattr, snode, i) {
if (!ebitmap_node_get_bit(snode, i)) ebitmap_for_each_positive_bit(tattr, tnode, j) {
continue;
ebitmap_for_each_bit(tattr, tnode, j) {
if (!ebitmap_node_get_bit(tnode, j))
continue;
avkey.source_type = i + 1; avkey.source_type = i + 1;
avkey.target_type = j + 1; avkey.target_type = j + 1;
for (node = avtab_search_node(&policydb.te_avtab, &avkey); for (node = avtab_search_node(&policydb.te_avtab, &avkey);
...@@ -1668,14 +1664,10 @@ int security_get_user_sids(u32 fromsid, ...@@ -1668,14 +1664,10 @@ int security_get_user_sids(u32 fromsid,
goto out_unlock; goto out_unlock;
} }
ebitmap_for_each_bit(&user->roles, rnode, i) { ebitmap_for_each_positive_bit(&user->roles, rnode, i) {
if (!ebitmap_node_get_bit(rnode, i))
continue;
role = policydb.role_val_to_struct[i]; role = policydb.role_val_to_struct[i];
usercon.role = i+1; usercon.role = i+1;
ebitmap_for_each_bit(&role->types, tnode, j) { ebitmap_for_each_positive_bit(&role->types, tnode, j) {
if (!ebitmap_node_get_bit(tnode, j))
continue;
usercon.type = j+1; usercon.type = j+1;
if (mls_setup_user_range(fromcon, user, &usercon)) if (mls_setup_user_range(fromcon, user, &usercon))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment