Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 782ebb99 authored by Stephen Smalley's avatar Stephen Smalley Committed by Linus Torvalds
Browse files

[PATCH] selinux: Reduce memory use by avtab

This patch improves memory use by SELinux by both reducing the avtab node
size and reducing the number of avtab nodes.  The memory savings are
substantial, e.g.  on a 64-bit system after boot, James Morris reported the
following data for the targeted and strict policies:

            #objs  objsize   kernmem
Targeted:
  Before:  237888       40     9.1MB
  After:    19968       24     468KB

Strict:
  Before:  571680       40   21.81MB
  After:   221052       24    5.06MB

The improvement in memory use comes at a cost in the speed of security
server computations of access vectors, but these computations are only
required on AVC cache misses, and performance measurements by James Morris
using a number of benchmarks have shown that the change does not cause any
significant degradation.

Note that a rebuilt policy via an updated policy toolchain
(libsepol/checkpolicy) is required in order to gain the full benefits of
this patch, although some memory savings benefits are immediately applied
even to older policies (in particular, the reduction in avtab node size).
Sources for the updated toolchain are presently available from the
sourceforge CVS tree (http://sourceforge.net/cvs/?group_id=21266), and
tarballs are available from http://www.flux.utah.edu/~sds

.

Signed-off-by: default avatarStephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 720d6c29
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -23,10 +23,11 @@
#define POLICYDB_VERSION_NLCLASS	18
#define POLICYDB_VERSION_VALIDATETRANS	19
#define POLICYDB_VERSION_MLS		19
#define POLICYDB_VERSION_AVTAB		20

/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN   POLICYDB_VERSION_BASE
#define POLICYDB_VERSION_MAX   POLICYDB_VERSION_MLS
#define POLICYDB_VERSION_MAX   POLICYDB_VERSION_AVTAB

#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
extern int selinux_enabled;
+123 −69
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
{
	int hvalue;
	struct avtab_node *prev, *cur, *newnode;
	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);

	if (!h)
		return -EINVAL;
@@ -69,7 +70,7 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class == cur->key.target_class &&
		    (datum->specified & cur->datum.specified))
		    (specified & cur->key.specified))
			return -EEXIST;
		if (key->source_type < cur->key.source_type)
			break;
@@ -98,6 +99,7 @@ avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_da
{
	int hvalue;
	struct avtab_node *prev, *cur, *newnode;
	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);

	if (!h)
		return NULL;
@@ -108,7 +110,7 @@ avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_da
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class == cur->key.target_class &&
		    (datum->specified & cur->datum.specified))
		    (specified & cur->key.specified))
			break;
		if (key->source_type < cur->key.source_type)
			break;
@@ -125,10 +127,11 @@ avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_da
	return newnode;
}

struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key, int specified)
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key)
{
	int hvalue;
	struct avtab_node *cur;
	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);

	if (!h)
		return NULL;
@@ -138,7 +141,7 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key, int spe
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class == cur->key.target_class &&
		    (specified & cur->datum.specified))
		    (specified & cur->key.specified))
			return &cur->datum;

		if (key->source_type < cur->key.source_type)
@@ -159,10 +162,11 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key, int spe
 * conjunction with avtab_search_next_node()
 */
struct avtab_node*
avtab_search_node(struct avtab *h, struct avtab_key *key, int specified)
avtab_search_node(struct avtab *h, struct avtab_key *key)
{
	int hvalue;
	struct avtab_node *cur;
	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);

	if (!h)
		return NULL;
@@ -172,7 +176,7 @@ avtab_search_node(struct avtab *h, struct avtab_key *key, int specified)
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class == cur->key.target_class &&
		    (specified & cur->datum.specified))
		    (specified & cur->key.specified))
			return cur;

		if (key->source_type < cur->key.source_type)
@@ -196,11 +200,12 @@ avtab_search_node_next(struct avtab_node *node, int specified)
	if (!node)
		return NULL;

	specified &= ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
	for (cur = node->next; cur; cur = cur->next) {
		if (node->key.source_type == cur->key.source_type &&
		    node->key.target_type == cur->key.target_type &&
		    node->key.target_class == cur->key.target_class &&
		    (specified & cur->datum.specified))
		    (specified & cur->key.specified))
			return cur;

		if (node->key.source_type < cur->key.source_type)
@@ -278,75 +283,126 @@ void avtab_hash_eval(struct avtab *h, char *tag)
	       max_chain_len);
}

int avtab_read_item(void *fp, struct avtab_datum *avdatum, struct avtab_key *avkey)
static uint16_t spec_order[] = {
	AVTAB_ALLOWED,
	AVTAB_AUDITDENY,
	AVTAB_AUDITALLOW,
	AVTAB_TRANSITION,
	AVTAB_CHANGE,
	AVTAB_MEMBER
};

int avtab_read_item(void *fp, u32 vers, struct avtab *a,
	            int (*insertf)(struct avtab *a, struct avtab_key *k,
				   struct avtab_datum *d, void *p),
		    void *p)
{
	u32 buf[7];
	u32 items, items2;
	int rc;
	u16 buf16[4], enabled;
	u32 buf32[7], items, items2, val;
	struct avtab_key key;
	struct avtab_datum datum;
	int i, rc;

	memset(avkey, 0, sizeof(struct avtab_key));
	memset(avdatum, 0, sizeof(struct avtab_datum));
	memset(&key, 0, sizeof(struct avtab_key));
	memset(&datum, 0, sizeof(struct avtab_datum));

	rc = next_entry(buf, fp, sizeof(u32));
	if (vers < POLICYDB_VERSION_AVTAB) {
		rc = next_entry(buf32, fp, sizeof(u32));
		if (rc < 0) {
			printk(KERN_ERR "security: avtab: truncated entry\n");
		goto bad;
			return -1;
		}
	items2 = le32_to_cpu(buf[0]);
	if (items2 > ARRAY_SIZE(buf)) {
		items2 = le32_to_cpu(buf32[0]);
		if (items2 > ARRAY_SIZE(buf32)) {
			printk(KERN_ERR "security: avtab: entry overflow\n");
		goto bad;
			return -1;

		}
	rc = next_entry(buf, fp, sizeof(u32)*items2);
		rc = next_entry(buf32, fp, sizeof(u32)*items2);
		if (rc < 0) {
			printk(KERN_ERR "security: avtab: truncated entry\n");
		goto bad;
			return -1;
		}
		items = 0;
	avkey->source_type = le32_to_cpu(buf[items++]);
	avkey->target_type = le32_to_cpu(buf[items++]);
	avkey->target_class = le32_to_cpu(buf[items++]);
	avdatum->specified = le32_to_cpu(buf[items++]);
	if (!(avdatum->specified & (AVTAB_AV | AVTAB_TYPE))) {
		printk(KERN_ERR "security: avtab: null entry\n");
		goto bad;

		val = le32_to_cpu(buf32[items++]);
		key.source_type = (u16)val;
		if (key.source_type != val) {
			printk("security: avtab: truncated source type\n");
			return -1;
		}
	if ((avdatum->specified & AVTAB_AV) &&
	    (avdatum->specified & AVTAB_TYPE)) {
		printk(KERN_ERR "security: avtab: entry has both access vectors and types\n");
		goto bad;
		val = le32_to_cpu(buf32[items++]);
		key.target_type = (u16)val;
		if (key.target_type != val) {
			printk("security: avtab: truncated target type\n");
			return -1;
		}
	if (avdatum->specified & AVTAB_AV) {
		if (avdatum->specified & AVTAB_ALLOWED)
			avtab_allowed(avdatum) = le32_to_cpu(buf[items++]);
		if (avdatum->specified & AVTAB_AUDITDENY)
			avtab_auditdeny(avdatum) = le32_to_cpu(buf[items++]);
		if (avdatum->specified & AVTAB_AUDITALLOW)
			avtab_auditallow(avdatum) = le32_to_cpu(buf[items++]);
	} else {
		if (avdatum->specified & AVTAB_TRANSITION)
			avtab_transition(avdatum) = le32_to_cpu(buf[items++]);
		if (avdatum->specified & AVTAB_CHANGE)
			avtab_change(avdatum) = le32_to_cpu(buf[items++]);
		if (avdatum->specified & AVTAB_MEMBER)
			avtab_member(avdatum) = le32_to_cpu(buf[items++]);
		val = le32_to_cpu(buf32[items++]);
		key.target_class = (u16)val;
		if (key.target_class != val) {
			printk("security: avtab: truncated target class\n");
			return -1;
		}
	if (items != items2) {
		printk(KERN_ERR "security: avtab: entry only had %d items, expected %d\n",
		       items2, items);
		goto bad;

		val = le32_to_cpu(buf32[items++]);
		enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;

		if (!(val & (AVTAB_AV | AVTAB_TYPE))) {
			printk("security: avtab: null entry\n");
			return -1;
		}
		if ((val & AVTAB_AV) &&
		    (val & AVTAB_TYPE)) {
			printk("security: avtab: entry has both access vectors and types\n");
			return -1;
		}

		for (i = 0; i < sizeof(spec_order)/sizeof(u16); i++) {
			if (val & spec_order[i]) {
				key.specified = spec_order[i] | enabled;
				datum.data = le32_to_cpu(buf32[items++]);
				rc = insertf(a, &key, &datum, p);
				if (rc) return rc;
			}
		}

		if (items != items2) {
			printk("security: avtab: entry only had %d items, expected %d\n", items2, items);
			return -1;
		}
		return 0;
bad:
	}

	rc = next_entry(buf16, fp, sizeof(u16)*4);
	if (rc < 0) {
		printk("security: avtab: truncated entry\n");
		return -1;
	}

	items = 0;
	key.source_type = le16_to_cpu(buf16[items++]);
	key.target_type = le16_to_cpu(buf16[items++]);
	key.target_class = le16_to_cpu(buf16[items++]);
	key.specified = le16_to_cpu(buf16[items++]);

	rc = next_entry(buf32, fp, sizeof(u32));
	if (rc < 0) {
		printk("security: avtab: truncated entry\n");
		return -1;
	}
	datum.data = le32_to_cpu(*buf32);
	return insertf(a, &key, &datum, p);
}

static int avtab_insertf(struct avtab *a, struct avtab_key *k,
			 struct avtab_datum *d, void *p)
{
	return avtab_insert(a, k, d);
}

int avtab_read(struct avtab *a, void *fp, u32 config)
int avtab_read(struct avtab *a, void *fp, u32 vers)
{
	int rc;
	struct avtab_key avkey;
	struct avtab_datum avdatum;
	u32 buf[1];
	u32 nel, i;

@@ -363,16 +419,14 @@ int avtab_read(struct avtab *a, void *fp, u32 config)
		goto bad;
	}
	for (i = 0; i < nel; i++) {
		if (avtab_read_item(fp, &avdatum, &avkey)) {
			rc = -EINVAL;
			goto bad;
		}
		rc = avtab_insert(a, &avkey, &avdatum);
		rc = avtab_read_item(fp,vers, a, avtab_insertf, NULL);
		if (rc) {
			if (rc == -ENOMEM)
				printk(KERN_ERR "security: avtab: out of memory\n");
			if (rc == -EEXIST)
			else if (rc == -EEXIST)
				printk(KERN_ERR "security: avtab: duplicate entry\n");
			else
				rc = -EINVAL;
			goto bad;
		}
	}
+18 −19
Original line number Diff line number Diff line
@@ -21,12 +21,9 @@
#define _SS_AVTAB_H_

struct avtab_key {
	u32 source_type;	/* source type */
	u32 target_type;	/* target type */
	u32 target_class;	/* target object class */
};

struct avtab_datum {
	u16 source_type;	/* source type */
	u16 target_type;	/* target type */
	u16 target_class;	/* target object class */
#define AVTAB_ALLOWED     1
#define AVTAB_AUDITALLOW  2
#define AVTAB_AUDITDENY   4
@@ -35,15 +32,13 @@ struct avtab_datum {
#define AVTAB_MEMBER     32
#define AVTAB_CHANGE     64
#define AVTAB_TYPE       (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
#define AVTAB_ENABLED    0x80000000 /* reserved for used in cond_avtab */
	u32 specified;	/* what fields are specified */
	u32 data[3];	/* access vectors or types */
#define avtab_allowed(x) (x)->data[0]
#define avtab_auditdeny(x) (x)->data[1]
#define avtab_auditallow(x) (x)->data[2]
#define avtab_transition(x) (x)->data[0]
#define avtab_change(x) (x)->data[1]
#define avtab_member(x) (x)->data[2]
#define AVTAB_ENABLED_OLD    0x80000000 /* reserved for used in cond_avtab */
#define AVTAB_ENABLED    0x8000 /* reserved for used in cond_avtab */
	u16 specified;	/* what field is specified */
};

struct avtab_datum {
	u32 data; /* access vector or type value */
};

struct avtab_node {
@@ -58,17 +53,21 @@ struct avtab {
};

int avtab_init(struct avtab *);
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k, int specified);
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k);
void avtab_destroy(struct avtab *h);
void avtab_hash_eval(struct avtab *h, char *tag);

int avtab_read_item(void *fp, struct avtab_datum *avdatum, struct avtab_key *avkey);
int avtab_read(struct avtab *a, void *fp, u32 config);
int avtab_read_item(void *fp, uint32_t vers, struct avtab *a,
		    int (*insert)(struct avtab *a, struct avtab_key *k,
				  struct avtab_datum *d, void *p),
		    void *p);

int avtab_read(struct avtab *a, void *fp, u32 vers);

struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key,
					  struct avtab_datum *datum);

struct avtab_node *avtab_search_node(struct avtab *h, struct avtab_key *key, int specified);
struct avtab_node *avtab_search_node(struct avtab *h, struct avtab_key *key);

struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified);

+114 −91
Original line number Diff line number Diff line
@@ -100,18 +100,18 @@ int evaluate_cond_node(struct policydb *p, struct cond_node *node)
		/* turn the rules on or off */
		for (cur = node->true_list; cur != NULL; cur = cur->next) {
			if (new_state <= 0) {
				cur->node->datum.specified &= ~AVTAB_ENABLED;
				cur->node->key.specified &= ~AVTAB_ENABLED;
			} else {
				cur->node->datum.specified |= AVTAB_ENABLED;
				cur->node->key.specified |= AVTAB_ENABLED;
			}
		}

		for (cur = node->false_list; cur != NULL; cur = cur->next) {
			/* -1 or 1 */
			if (new_state) {
				cur->node->datum.specified &= ~AVTAB_ENABLED;
				cur->node->key.specified &= ~AVTAB_ENABLED;
			} else {
				cur->node->datum.specified |= AVTAB_ENABLED;
				cur->node->key.specified |= AVTAB_ENABLED;
			}
		}
	}
@@ -252,40 +252,30 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp)
	return -1;
}

static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list,
			     struct cond_av_list *other)
struct cond_insertf_data
{
	struct cond_av_list *list, *last = NULL, *cur;
	struct avtab_key key;
	struct avtab_datum datum;
	struct policydb *p;
	struct cond_av_list *other;
	struct cond_av_list *head;
	struct cond_av_list *tail;
};

static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *ptr)
{
	struct cond_insertf_data *data = ptr;
	struct policydb *p = data->p;
	struct cond_av_list *other = data->other, *list, *cur;
	struct avtab_node *node_ptr;
	int rc;
	u32 buf[1], i, len;
	u8 found;

	*ret_list = NULL;

	len = 0;
	rc = next_entry(buf, fp, sizeof buf);
	if (rc < 0)
		return -1;

	len = le32_to_cpu(buf[0]);
	if (len == 0) {
		return 0;
	}

	for (i = 0; i < len; i++) {
		if (avtab_read_item(fp, &datum, &key))
			goto err;

	/*
	 * For type rules we have to make certain there aren't any
	 * conflicting rules by searching the te_avtab and the
	 * cond_te_avtab.
	 */
		if (datum.specified & AVTAB_TYPE) {
			if (avtab_search(&p->te_avtab, &key, AVTAB_TYPE)) {
	if (k->specified & AVTAB_TYPE) {
		if (avtab_search(&p->te_avtab, k)) {
			printk("security: type rule already exists outside of a conditional.");
			goto err;
		}
@@ -298,9 +288,9 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list *
		 * be any other entries.
		 */
		if (other) {
				node_ptr = avtab_search_node(&p->te_cond_avtab, &key, AVTAB_TYPE);
			node_ptr = avtab_search_node(&p->te_cond_avtab, k);
			if (node_ptr) {
					if (avtab_search_node_next(node_ptr, AVTAB_TYPE)) {
				if (avtab_search_node_next(node_ptr, k->specified)) {
					printk("security: too many conflicting type rules.");
					goto err;
				}
@@ -312,18 +302,19 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list *
					}
				}
				if (!found) {
						printk("security: conflicting type rules.");
					printk("security: conflicting type rules.\n");
					goto err;
				}
			}
		} else {
				if (avtab_search(&p->te_cond_avtab, &key, AVTAB_TYPE)) {
					printk("security: conflicting type rules when adding type rule for true.");
			if (avtab_search(&p->te_cond_avtab, k)) {
				printk("security: conflicting type rules when adding type rule for true.\n");
				goto err;
			}
		}
	}
		node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, &key, &datum);

	node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);
	if (!node_ptr) {
		printk("security: could not insert rule.");
		goto err;
@@ -332,22 +323,53 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list *
	list = kmalloc(sizeof(struct cond_av_list), GFP_KERNEL);
	if (!list)
		goto err;
		memset(list, 0, sizeof(struct cond_av_list));
	memset(list, 0, sizeof(*list));

	list->node = node_ptr;
		if (i == 0)
			*ret_list = list;
	if (!data->head)
		data->head = list;
	else
			last->next = list;
		last = list;
		data->tail->next = list;
	data->tail = list;
	return 0;

err:
	cond_av_list_destroy(data->head);
	data->head = NULL;
	return -1;
}

	return 0;
err:
	cond_av_list_destroy(*ret_list);
static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other)
{
	int i, rc;
	u32 buf[1], len;
	struct cond_insertf_data data;

	*ret_list = NULL;

	len = 0;
	rc = next_entry(buf, fp, sizeof(u32));
	if (rc < 0)
		return -1;

	len = le32_to_cpu(buf[0]);
	if (len == 0) {
		return 0;
	}

	data.p = p;
	data.other = other;
	data.head = NULL;
	data.tail = NULL;
	for (i = 0; i < len; i++) {
		rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab, cond_insertf, &data);
		if (rc)
			return rc;

	}

	*ret_list = data.head;
	return 0;
}

static int expr_isvalid(struct policydb *p, struct cond_expr *expr)
@@ -452,6 +474,7 @@ int cond_read_list(struct policydb *p, void *fp)
	return 0;
err:
	cond_list_destroy(p->cond_list);
	p->cond_list = NULL;
	return -1;
}

@@ -465,22 +488,22 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decisi
	if(!ctab || !key || !avd)
		return;

	for(node = avtab_search_node(ctab, key, AVTAB_AV); node != NULL;
				node = avtab_search_node_next(node, AVTAB_AV)) {
		if ( (__u32) (AVTAB_ALLOWED|AVTAB_ENABLED) ==
		     (node->datum.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
			avd->allowed |= avtab_allowed(&node->datum);
		if ( (__u32) (AVTAB_AUDITDENY|AVTAB_ENABLED) ==
		     (node->datum.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
	for(node = avtab_search_node(ctab, key); node != NULL;
				node = avtab_search_node_next(node, key->specified)) {
		if ( (u16) (AVTAB_ALLOWED|AVTAB_ENABLED) ==
		     (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
			avd->allowed |= node->datum.data;
		if ( (u16) (AVTAB_AUDITDENY|AVTAB_ENABLED) ==
		     (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
			/* Since a '0' in an auditdeny mask represents a
			 * permission we do NOT want to audit (dontaudit), we use
			 * the '&' operand to ensure that all '0's in the mask
			 * are retained (much unlike the allow and auditallow cases).
			 */
			avd->auditdeny &= avtab_auditdeny(&node->datum);
		if ( (__u32) (AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
		     (node->datum.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
			avd->auditallow |= avtab_auditallow(&node->datum);
			avd->auditdeny &= node->datum.data;
		if ( (u16) (AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
		     (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
			avd->auditallow |= node->datum.data;
	}
	return;
}
+30 −0
Original line number Diff line number Diff line
@@ -32,11 +32,41 @@ struct ebitmap {
#define ebitmap_length(e) ((e)->highbit)
#define ebitmap_startbit(e) ((e)->node ? (e)->node->startbit : 0)

static inline unsigned int ebitmap_start(struct ebitmap *e,
					 struct ebitmap_node **n)
{
	*n = e->node;
	return ebitmap_startbit(e);
}

static inline void ebitmap_init(struct ebitmap *e)
{
	memset(e, 0, sizeof(*e));
}

static inline unsigned int ebitmap_next(struct ebitmap_node **n,
					unsigned int bit)
{
	if ((bit == ((*n)->startbit + MAPSIZE - 1)) &&
	    (*n)->next) {
		*n = (*n)->next;
		return (*n)->startbit;
	}

	return (bit+1);
}

static inline int ebitmap_node_get_bit(struct ebitmap_node * n,
				       unsigned int bit)
{
	if (n->map & (MAPBIT << (bit - n->startbit)))
		return 1;
	return 0;
}

#define ebitmap_for_each_bit(e, n, bit) \
	for (bit = ebitmap_start(e, &n); bit < ebitmap_length(e); bit = ebitmap_next(&n, bit)) \

int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2);
int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src);
int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2);
Loading