
From: James Morris <jmorris@redhat.com>

The following patch improves the scalability of SELinux by replacing the
global avc_lock with an RCU based scheme by Kaigai Kohei.  The size of the
cache is made tunable, to allow administrators to tune systems for different
workloads, while statistics are exported via selinuxfs to allow AVC
performance to be monitored at a low level.

AVC nodes are also allocated now via a slab cache, and AVC references have
been removed from the code.

This code has been extensively tested and benchmarked (see benchmark results
below).  Baseline performance is not improved, although it is clear that
dramatic scalability improvements are achieved.

Baseline performance and networking scalability are areas where work is
ongoing (in particular, we need to add caching of some network security
objects so that we don't fallback to policy database lookups on each
permission call).

Benchmark results:

===============================================================================================

System: 4 node 16-way IA64 NUMA

- 'Stream' is based on http://www.cs.virginia.edu/stream/ , HPC memory bandwidth test,
  higher result is better.
- Hackbench: scheduler scalability benchmark by Rusty, lower is better.

Standard kernel:
  2.6.9-1.648_EL  SELINUX=0 : Stream 6159.987MB/s HackBench 53.144
  2.6.9-1.648_EL  SELINUX=1 : Stream 5872.529MB/s HackBench 1043.132
  
Kernel with RCU/AVC patches:
  2.6.9-1.689_avcrcu.root SELINUX=0 : Stream 8829.647MB/s HackBench 53.976
  2.6.9-1.689_avcrcu.root SELINUX=1 : Stream 8817.117MB/s HackBench 50.975

===============================================================================================

System: 8-way PIII 900Mhz Xeon with 9GB RAM
Fileystem: ext2 for all testing.

Notes:
    AVC was reset before tests, so avc was flushed.
    System was run in enforcing mode.

Key:
    std-nolsm:      standard kernel with LSM disabled
    std-lsmcap:     standard kernel with LSM enabled, capabilities LSM
    std-sel-strict: standard kernel with SELinux enabled, capabilities secondary LSM
    rcu-sel-strict: as above with RCU & AVC stats patches

---------------------------------------------------------------------------------------
Dbench
---------------------------------------------------------------------------------------
Notes:
  100 iterations each, results are throughput in MB/sec (bigger is better)

----------------------------------------------------------------
# Clients		1	2	4	8
----------------------------------------------------------------
std-nolsm		151.1	270.3	460.4	699.0
std-lsmcap		152.6	273.3	465.4	703.5
std-sel-strict		142.4	254.5	428.1	598.5
rcu-sel-strict		143.3	261.0	437.1	680.7
----------------------------------------------------------------

---------------------------------------------------------------------------------------
Unixbench
---------------------------------------------------------------------------------------
Notes:
  Run with parameters '-10 index'.
  Overall score is an index, higher is better.
  

std-nolsm		328.0
std-lsmcap		327.9
std-sel-strict		311.1
rcu-sel-strict		305.9

---------------------------------------------------------------------------------------
Apachebench
---------------------------------------------------------------------------------------

Notes:
  Loopback, httpd logging to /dev/null.
  Client and server on same system (hence peaking before 8 clients).
  Document size is 500kB, 20,000 requests.
  Values are transfer rate in Kb/s received: bigger is better.

---------------------------------------------------------------------------------
Concurrency Level	1		2		4		8
---------------------------------------------------------------------------------
std-nolsm		43083.26	49740.85	48657.74	47913.67
std-lsmcap		43888.87	48943.08	49475.39	48657.81
std-sel-strict		33551.52	35082.17	34854.14	34363.09
rcu-sel-strict		33118.51	36509.20	36621.59	35903.92
---------------------------------------------------------------------------------

---------------------------------------------------------------------------------------
Webstone
---------------------------------------------------------------------------------------

Notes:
  Two physical clients, each via private GigE links.
  Result is total server throughput in Mb/s (bigger is better).
  1 iteration for two minutes.
  
----------------------------------------------------------------
# Clients		2	4	8	16	32
----------------------------------------------------------------
std-nolsm		67.79	284.43	374.35	420.09	427.09
std-lsmcap		67.79	298.15	373.50	420.84	431.80
std-sel-strict		78.42	272.48	345.99	378.81	376.30
rcu-sel-strict		77.53	268.91	348.65	390.76	391.35
----------------------------------------------------------------

===============================================================================================

Testing by KaiGai on a 32-way system during development:
---------------------------------------------------------

The following three benchmark programs were used.
* parallel write() system call (my original)
  - The results of 2.6.9.RCU scale linearly as 2.6.9(disable)
* dbench
  - The results of 2.6.9.RCU are almost same as 2.6.9(disable).
    The performance turned down in 32CPU, but I think it's a
    problem with dbench. (I'm not so fine about dbench configuration :( )
* UNIX bench(micro benchmark)
  - 2.6.9.RCU doesn't have a significant degration toward 2.6.9(enable)
These are as expected.

The raw results:
---- write() system call [Kcalls/sec] ---------------------
Num of CPUs     -1CPU- -2CPU- -4CPU- -8CPU- -16CPU- -32CPU-
2.6.9(disable)   773.6 1611.4 3160.5 6301.0 12605.0 24296.4
2.6.9(enable)    535.8  655.8  241.8  127.7    62.9    30.2
2.6.9.RCU        542.5 1042.9 2301.6 4518.0  8963.5 18033.6
-----------------------------------------------------------

---- dbench Through-put [MB/sec] --------------------------
Num of CPUs     -1CPU- -2CPU- -4CPU- -8CPU- -16CPU- -32CPU-
2.6.9(disable)   286.7  556.9 1056.0 1572.1  1663.1   395.5
2.6.9(enable)    268.7  500.5  771.4  381.3   164.3    84.2
2.6.9.RCU        270.0  524.2  994.8 1511.3  1489.5   386.9
-----------------------------------------------------------

---- UNIX bench Results [Index value] ---------------------
                     2.6.9(disable) 2.6.9(enable) 2.6.9.RCU
Dhrystone 2                   275.8       275.8       275.8
D-P Whetstone                  97.5        97.5        97.5
Execl Throughput              408.9       398.3       397.5
File Copy 1KB x  2000 blks    604.2       520.4       517.5
File Copy 256B x  500 blks    510.9       414.8       415.1
File Copy 4KB x  8000 blks    980.4       893.5       878.2
Pipe Throughput               510.4       403.5       396.9
Process Creation              253.5       251.2       249.7
Shell Scripts(8 concurrent)  1190.5      1165.0      1182.2
System Call Overhead          468.7       469.9       469.0
-----------------------------------------------------------
     FINAL SCORE              434.6       403.6       402.2


===============================================================================================

Signed-off-by: Kaigai Kohei <kaigai@ak.jp.nec.com>
Signed-off-by: Stephen Smalley <sds@epoch.ncsc.mil>
Signed-off-by: James Morris <jmorris@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/security/selinux/avc.c            |  472 +++++++++++++++---------------
 25-akpm/security/selinux/hooks.c          |  162 ++++------
 25-akpm/security/selinux/include/avc.h    |   23 -
 25-akpm/security/selinux/include/objsec.h |    6 
 25-akpm/security/selinux/selinuxfs.c      |    2 
 5 files changed, 312 insertions(+), 353 deletions(-)

diff -puN security/selinux/avc.c~selinux-scalability-convert-avc-to-rcu security/selinux/avc.c
--- 25/security/selinux/avc.c~selinux-scalability-convert-avc-to-rcu	2004-11-30 01:23:18.073019592 -0800
+++ 25-akpm/security/selinux/avc.c	2004-11-30 01:23:18.088017312 -0800
@@ -4,6 +4,9 @@
  * Authors:  Stephen Smalley, <sds@epoch.ncsc.mil>
  *           James Morris <jmorris@redhat.com>
  *
+ * Update:   KaiGai, Kohei <kaigai@ak.jp.nec.com>
+ *     Replaced the avc_lock spinlock by RCU.
+ *
  * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
  *
  *	This program is free software; you can redistribute it and/or modify
@@ -36,26 +39,29 @@
 #include "objsec.h"
 
 #define AVC_CACHE_SLOTS		512
-#define AVC_CACHE_MAXNODES	410
+#define AVC_CACHE_THRESHOLD	512
+#define AVC_CACHE_RECLAIM	16
 
 struct avc_entry {
 	u32			ssid;
 	u32			tsid;
 	u16			tclass;
 	struct av_decision	avd;
-	int			used;	/* used recently */
+	atomic_t		used;	/* used recently */
 };
 
 struct avc_node {
 	struct avc_entry	ae;
-	struct avc_node		*next;
+	struct list_head	list;
+	struct rcu_head         rhead;
 };
 
 struct avc_cache {
-	struct avc_node	*slots[AVC_CACHE_SLOTS];
-	u32		lru_hint;	/* LRU hint for reclaim scan */
-	u32		active_nodes;
-	u32		latest_notif;	/* latest revocation notification */
+	struct list_head	slots[AVC_CACHE_SLOTS];
+	spinlock_t		slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
+	atomic_t		lru_hint;	/* LRU hint for reclaim scan */
+	atomic_t		active_nodes;
+	u32			latest_notif;	/* latest revocation notification */
 };
 
 struct avc_callback_node {
@@ -70,11 +76,10 @@ struct avc_callback_node {
 	struct avc_callback_node *next;
 };
 
-static spinlock_t avc_lock = SPIN_LOCK_UNLOCKED;
-static struct avc_node *avc_node_freelist;
 static struct avc_cache avc_cache;
 static unsigned avc_cache_stats[AVC_NSTATS];
 static struct avc_callback_node *avc_callbacks;
+static kmem_cache_t *avc_node_cachep;
 
 static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
 {
@@ -188,20 +193,17 @@ void avc_dump_query(struct audit_buffer 
  */
 void __init avc_init(void)
 {
-	struct avc_node	*new;
 	int i;
 
-	for (i = 0; i < AVC_CACHE_MAXNODES; i++) {
-		new = kmalloc(sizeof(*new), GFP_ATOMIC);
-		if (!new) {
-			printk(KERN_WARNING "avc:  only able to allocate "
-			       "%d entries\n", i);
-			break;
-		}
-		memset(new, 0, sizeof(*new));
-		new->next = avc_node_freelist;
-		avc_node_freelist = new;
+	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+		INIT_LIST_HEAD(&avc_cache.slots[i]);
+		avc_cache.slots_lock[i] = SPIN_LOCK_UNLOCKED;
 	}
+	atomic_set(&avc_cache.active_nodes, 0);
+	atomic_set(&avc_cache.lru_hint, 0);
+
+	avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
+					     0, SLAB_PANIC, NULL, NULL);
 
 	audit_log(current->audit_context, "AVC INITIALIZED\n");
 }
@@ -211,120 +213,130 @@ static void avc_hash_eval(char *tag)
 {
 	int i, chain_len, max_chain_len, slots_used;
 	struct avc_node *node;
-	unsigned long flags;
 
-	spin_lock_irqsave(&avc_lock,flags);
+	rcu_read_lock();
 
 	slots_used = 0;
 	max_chain_len = 0;
 	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
-		node = avc_cache.slots[i];
-		if (node) {
+		if (!list_empty(&avc_cache.slots[i])) {
 			slots_used++;
 			chain_len = 0;
-			while (node) {
+			list_for_each_entry_rcu(node, &avc_cache.slots[i], list)
 				chain_len++;
-				node = node->next;
-			}
 			if (chain_len > max_chain_len)
 				max_chain_len = chain_len;
 		}
 	}
 
-	spin_unlock_irqrestore(&avc_lock,flags);
+	rcu_read_unlock();
 
 	printk(KERN_INFO "\n");
 	printk(KERN_INFO "%s avc:  %d entries and %d/%d buckets used, longest "
-	       "chain length %d\n", tag, avc_cache.active_nodes, slots_used,
-	       AVC_CACHE_SLOTS, max_chain_len);
+	       "chain length %d\n", tag, atomic_read(&avc_cache.active_nodes),
+		slots_used, AVC_CACHE_SLOTS, max_chain_len);
 }
 #else
 static inline void avc_hash_eval(char *tag)
 { }
 #endif
 
-static inline struct avc_node *avc_reclaim_node(void)
+static void avc_node_free(struct rcu_head *rhead)
 {
-	struct avc_node *prev, *cur;
-	int hvalue, try;
-
-	hvalue = avc_cache.lru_hint;
-	for (try = 0; try < 2; try++) {
-		do {
-			prev = NULL;
-			cur = avc_cache.slots[hvalue];
-			while (cur) {
-				if (!cur->ae.used)
-					goto found;
+	struct avc_node *node = container_of(rhead, struct avc_node, rhead);
+	kmem_cache_free(avc_node_cachep, node);
+}
 
-				cur->ae.used = 0;
+static void avc_node_delete(struct avc_node *node)
+{
+	list_del_rcu(&node->list);
+	call_rcu(&node->rhead, avc_node_free);
+	atomic_dec(&avc_cache.active_nodes);
+}
 
-				prev = cur;
-				cur = cur->next;
-			}
-			hvalue = (hvalue + 1) & (AVC_CACHE_SLOTS - 1);
-		} while (hvalue != avc_cache.lru_hint);
-	}
+static void avc_node_replace(struct avc_node *new, struct avc_node *old)
+{
+	list_replace_rcu(&old->list, &new->list);
+	call_rcu(&old->rhead, avc_node_free);
+	atomic_dec(&avc_cache.active_nodes);
+}
 
-	panic("avc_reclaim_node");
+static inline int avc_reclaim_node(void)
+{
+	struct avc_node *node;
+	int hvalue, try, ecx;
+	unsigned long flags;
 
-found:
-	avc_cache.lru_hint = hvalue;
+	for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++ ) {
+		hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1);
 
-	if (prev == NULL)
-		avc_cache.slots[hvalue] = cur->next;
-	else
-		prev->next = cur->next;
+		if (!spin_trylock_irqsave(&avc_cache.slots_lock[hvalue], flags))
+			continue;
 
-	return cur;
+		list_for_each_entry(node, &avc_cache.slots[hvalue], list) {
+			if (!atomic_dec_and_test(&node->ae.used)) {
+				/* Recently Unused */
+				avc_node_delete(node);
+				ecx++;
+				if (ecx >= AVC_CACHE_RECLAIM) {
+					spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
+					goto out;
+				}
+			}
+		}
+		spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
+	}
+out:
+	return ecx;
 }
 
-static inline struct avc_node *avc_claim_node(u32 ssid,
-                                              u32 tsid, u16 tclass)
+static struct avc_node *avc_alloc_node(void)
 {
-	struct avc_node *new;
-	int hvalue;
+	struct avc_node *node;
 
-	hvalue = avc_hash(ssid, tsid, tclass);
-	if (avc_node_freelist) {
-		new = avc_node_freelist;
-		avc_node_freelist = avc_node_freelist->next;
-		avc_cache.active_nodes++;
-	} else {
-		new = avc_reclaim_node();
-		if (!new)
-			goto out;
-	}
+	node = kmem_cache_alloc(avc_node_cachep, SLAB_ATOMIC);
+	if (!node)
+		goto out;
+
+	memset(node, 0, sizeof(*node));
+	INIT_RCU_HEAD(&node->rhead);
+	INIT_LIST_HEAD(&node->list);
+	atomic_set(&node->ae.used, 1);
 
-	new->ae.used = 1;
-	new->ae.ssid = ssid;
-	new->ae.tsid = tsid;
-	new->ae.tclass = tclass;
-	new->next = avc_cache.slots[hvalue];
-	avc_cache.slots[hvalue] = new;
+	if (atomic_inc_return(&avc_cache.active_nodes) > AVC_CACHE_THRESHOLD)
+		avc_reclaim_node();
 
 out:
-	return new;
+	return node;
+}
+
+static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
+{
+	node->ae.ssid = ssid;
+	node->ae.tsid = tsid;
+	node->ae.tclass = tclass;
+	memcpy(&node->ae.avd, &ae->avd, sizeof(node->ae.avd));
 }
 
 static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid,
                                                u16 tclass, int *probes)
 {
-	struct avc_node *cur;
+	struct avc_node *node, *ret = NULL;
 	int hvalue;
 	int tprobes = 1;
 
 	hvalue = avc_hash(ssid, tsid, tclass);
-	cur = avc_cache.slots[hvalue];
-	while (cur != NULL &&
-	       (ssid != cur->ae.ssid ||
-		tclass != cur->ae.tclass ||
-		tsid != cur->ae.tsid)) {
+	list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) {
+		if (ssid == node->ae.ssid &&
+		    tclass == node->ae.tclass &&
+		    tsid == node->ae.tsid) {
+			ret = node;
+			break;
+		}
 		tprobes++;
-		cur = cur->next;
 	}
 
-	if (cur == NULL) {
+	if (ret == NULL) {
 		/* cache miss */
 		goto out;
 	}
@@ -332,11 +344,10 @@ static inline struct avc_node *avc_searc
 	/* cache hit */
 	if (probes)
 		*probes = tprobes;
-
-	cur->ae.used = 1;
-
+	if (atomic_read(&ret->ae.used) != 1)
+		atomic_set(&ret->ae.used, 1);
 out:
-	return cur;
+	return ret;
 }
 
 /**
@@ -345,21 +356,18 @@ out:
  * @tsid: target security identifier
  * @tclass: target security class
  * @requested: requested permissions, interpreted based on @tclass
- * @aeref:  AVC entry reference
  *
  * Look up an AVC entry that is valid for the
  * @requested permissions between the SID pair
  * (@ssid, @tsid), interpreting the permissions
  * based on @tclass.  If a valid AVC entry exists,
- * then this function updates @aeref to refer to the
- * entry and returns %0. Otherwise, this function
- * returns -%ENOENT.
+ * then this function return the avc_node.
+ * Otherwise, this function returns NULL.
  */
-int avc_lookup(u32 ssid, u32 tsid, u16 tclass,
-               u32 requested, struct avc_entry_ref *aeref)
+static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass, u32 requested)
 {
 	struct avc_node *node;
-	int probes, rc = 0;
+	int probes;
 
 	avc_cache_stats_incr(AVC_CAV_LOOKUPS);
 	node = avc_search_node(ssid, tsid, tclass,&probes);
@@ -367,14 +375,35 @@ int avc_lookup(u32 ssid, u32 tsid, u16 t
 	if (node && ((node->ae.avd.decided & requested) == requested)) {
 		avc_cache_stats_incr(AVC_CAV_HITS);
 		avc_cache_stats_add(AVC_CAV_PROBES,probes);
-		aeref->ae = &node->ae;
 		goto out;
 	}
 
+	node = NULL;
 	avc_cache_stats_incr(AVC_CAV_MISSES);
-	rc = -ENOENT;
 out:
-	return rc;
+	return node;
+}
+
+static int avc_latest_notif_update(int seqno, int is_insert)
+{
+	int ret = 0;
+	static spinlock_t notif_lock = SPIN_LOCK_UNLOCKED;
+	unsigned long flag;
+
+	spin_lock_irqsave(&notif_lock, flag);
+	if (is_insert) {
+		if (seqno < avc_cache.latest_notif) {
+			printk(KERN_WARNING "avc:  seqno %d < latest_notif %d\n",
+			       seqno, avc_cache.latest_notif);
+			ret = -EAGAIN;
+		}
+	} else {
+		if (seqno > avc_cache.latest_notif)
+			avc_cache.latest_notif = seqno;
+	}
+	spin_unlock_irqrestore(&notif_lock, flag);
+
+	return ret;
 }
 
 /**
@@ -383,7 +412,6 @@ out:
  * @tsid: target security identifier
  * @tclass: target security class
  * @ae: AVC entry
- * @aeref:  AVC entry reference
  *
  * Insert an AVC entry for the SID pair
  * (@ssid, @tsid) and class @tclass.
@@ -392,37 +420,38 @@ out:
  * response to a security_compute_av() call.  If the
  * sequence number @ae->avd.seqno is not less than the latest
  * revocation notification, then the function copies
- * the access vectors into a cache entry, updates
- * @aeref to refer to the entry, and returns %0.
- * Otherwise, this function returns -%EAGAIN.
+ * the access vectors into a cache entry, returns
+ * avc_node inserted. Otherwise, this function returns NULL.
  */
-int avc_insert(u32 ssid, u32 tsid, u16 tclass,
-               struct avc_entry *ae, struct avc_entry_ref *aeref)
+static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
 {
-	struct avc_node *node;
-	int rc = 0;
+	struct avc_node *pos, *node = NULL;
+	int hvalue;
+	unsigned long flag;
 
-	if (ae->avd.seqno < avc_cache.latest_notif) {
-		printk(KERN_WARNING "avc:  seqno %d < latest_notif %d\n",
-		       ae->avd.seqno, avc_cache.latest_notif);
-		rc = -EAGAIN;
+	if (avc_latest_notif_update(ae->avd.seqno, 1))
 		goto out;
-	}
 
-	node = avc_claim_node(ssid, tsid, tclass);
-	if (!node) {
-		rc = -ENOMEM;
-		goto out;
+	node = avc_alloc_node();
+	if (node) {
+		hvalue = avc_hash(ssid, tsid, tclass);
+		avc_node_populate(node, ssid, tsid, tclass, ae);
+
+		spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
+		list_for_each_entry(pos, &avc_cache.slots[hvalue], list) {
+			if (pos->ae.ssid == ssid &&
+			    pos->ae.tsid == tsid &&
+			    pos->ae.tclass == tclass) {
+			    	avc_node_replace(node, pos);
+				goto found;
+			}
+		}
+		list_add_rcu(&node->list, &avc_cache.slots[hvalue]);
+found:
+		spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
 	}
-
-	node->ae.avd.allowed = ae->avd.allowed;
-	node->ae.avd.decided = ae->avd.decided;
-	node->ae.avd.auditallow = ae->avd.auditallow;
-	node->ae.avd.auditdeny = ae->avd.auditdeny;
-	node->ae.avd.seqno = ae->avd.seqno;
-	aeref->ae = &node->ae;
 out:
-	return rc;
+	return node;
 }
 
 static inline void avc_print_ipv6_addr(struct audit_buffer *ab,
@@ -686,8 +715,52 @@ static inline int avc_sidcmp(u32 x, u32 
 	return (x == y || x == SECSID_WILD || y == SECSID_WILD);
 }
 
-static inline void avc_update_node(u32 event, struct avc_node *node, u32 perms)
+/**
+ * avc_update_node Update an AVC entry
+ * @event : Updating event
+ * @perms : Permission mask bits
+ * @ssid,@tsid,@tclass : identifier of an AVC entry
+ *
+ * if a valid AVC entry doesn't exist,this function returns -ENOENT.
+ * if kmalloc() called internal returns NULL, this function returns -ENOMEM.
+ * otherwise, this function update the AVC entry. The original AVC-entry object
+ * will release later by RCU.
+ */
+static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
 {
+	int hvalue, rc = 0;
+	unsigned long flag;
+	struct avc_node *pos, *node, *orig = NULL;
+
+	/* Lock the target slot */
+	hvalue = avc_hash(ssid, tsid, tclass);
+	spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
+
+	list_for_each_entry(pos, &avc_cache.slots[hvalue], list){
+		if ( ssid==pos->ae.ssid &&
+		     tsid==pos->ae.tsid &&
+		     tclass==pos->ae.tclass ){
+			orig = pos;
+			break;
+		}
+	}
+
+	if (!orig) {
+		rc = -ENOENT;
+		goto out;
+	}
+
+	/*
+	 * Copy and replace original node.
+	 */
+	node = avc_alloc_node();
+	if (!node) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	avc_node_populate(node, ssid, tsid, tclass, &orig->ae);
+
 	switch (event) {
 	case AVC_CALLBACK_GRANT:
 		node->ae.avd.allowed |= perms;
@@ -709,6 +782,11 @@ static inline void avc_update_node(u32 e
 		node->ae.avd.auditdeny &= ~perms;
 		break;
 	}
+	avc_node_replace(node, orig);
+out:
+	spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
+
+	return rc;
 }
 
 static int avc_update_cache(u32 event, u32 ssid, u32 tsid,
@@ -716,31 +794,27 @@ static int avc_update_cache(u32 event, u
 {
 	struct avc_node *node;
 	int i;
-	unsigned long flags;
 
-	spin_lock_irqsave(&avc_lock,flags);
+	rcu_read_lock();
 
 	if (ssid == SECSID_WILD || tsid == SECSID_WILD) {
 		/* apply to all matching nodes */
 		for (i = 0; i < AVC_CACHE_SLOTS; i++) {
-			for (node = avc_cache.slots[i]; node;
-			     node = node->next) {
+			list_for_each_entry_rcu(node, &avc_cache.slots[i], list) {
 				if (avc_sidcmp(ssid, node->ae.ssid) &&
 				    avc_sidcmp(tsid, node->ae.tsid) &&
-				    tclass == node->ae.tclass) {
-					avc_update_node(event,node,perms);
+				    tclass == node->ae.tclass ) {
+					avc_update_node(event, perms, node->ae.ssid,
+							node->ae.tsid, node->ae.tclass);
 				}
 			}
 		}
 	} else {
 		/* apply to one node */
-		node = avc_search_node(ssid, tsid, tclass, NULL);
-		if (node) {
-			avc_update_node(event,node,perms);
-		}
+		avc_update_node(event, perms, ssid, tsid, tclass);
 	}
 
-	spin_unlock_irqrestore(&avc_lock,flags);
+	rcu_read_unlock();
 
 	return 0;
 }
@@ -752,7 +826,6 @@ static int avc_control(u32 event, u32 ss
 	struct avc_callback_node *c;
 	u32 tretained = 0, cretained = 0;
 	int rc = 0;
-	unsigned long flags;
 
 	/*
 	 * try_revoke only removes permissions from the cache
@@ -787,10 +860,7 @@ static int avc_control(u32 event, u32 ss
 		*out_retained = tretained;
 	}
 
-	spin_lock_irqsave(&avc_lock,flags);
-	if (seqno > avc_cache.latest_notif)
-		avc_cache.latest_notif = seqno;
-	spin_unlock_irqrestore(&avc_lock,flags);
+	avc_latest_notif_update(seqno, 0);
 
 out:
 	return rc;
@@ -857,32 +927,17 @@ int avc_ss_reset(u32 seqno)
 {
 	struct avc_callback_node *c;
 	int i, rc = 0;
-	struct avc_node *node, *tmp;
-	unsigned long flags;
+	unsigned long flag;
+	struct avc_node *node;
 
 	avc_hash_eval("reset");
 
-	spin_lock_irqsave(&avc_lock,flags);
-
 	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
-		node = avc_cache.slots[i];
-		while (node) {
-			tmp = node;
-			node = node->next;
-			tmp->ae.ssid = tmp->ae.tsid = SECSID_NULL;
-			tmp->ae.tclass = SECCLASS_NULL;
-			tmp->ae.avd.allowed = tmp->ae.avd.decided = 0;
-			tmp->ae.avd.auditallow = tmp->ae.avd.auditdeny = 0;
-			tmp->ae.used = 0;
-			tmp->next = avc_node_freelist;
-			avc_node_freelist = tmp;
-			avc_cache.active_nodes--;
-		}
-		avc_cache.slots[i] = NULL;
+		spin_lock_irqsave(&avc_cache.slots_lock[i], flag);
+		list_for_each_entry(node, &avc_cache.slots[i], list)
+			avc_node_delete(node);
+		spin_unlock_irqrestore(&avc_cache.slots_lock[i], flag);
 	}
-	avc_cache.lru_hint = 0;
-
-	spin_unlock_irqrestore(&avc_lock,flags);
 
 	for (i = 0; i < AVC_NSTATS; i++)
 		avc_cache_stats[i] = 0;
@@ -896,10 +951,7 @@ int avc_ss_reset(u32 seqno)
 		}
 	}
 
-	spin_lock_irqsave(&avc_lock,flags);
-	if (seqno > avc_cache.latest_notif)
-		avc_cache.latest_notif = seqno;
-	spin_unlock_irqrestore(&avc_lock,flags);
+	avc_latest_notif_update(seqno, 0);
 out:
 	return rc;
 }
@@ -950,14 +1002,12 @@ int avc_ss_set_auditdeny(u32 ssid, u32 t
  * @tsid: target security identifier
  * @tclass: target security class
  * @requested: requested permissions, interpreted based on @tclass
- * @aeref:  AVC entry reference
  * @avd: access vector decisions
  *
  * Check the AVC to determine whether the @requested permissions are granted
  * for the SID pair (@ssid, @tsid), interpreting the permissions
  * based on @tclass, and call the security server on a cache miss to obtain
- * a new decision and add it to the cache.  Update @aeref to refer to an AVC
- * entry with the resulting decisions, and return a copy of the decisions
+ * a new decision and add it to the cache.  Return a copy of the decisions
  * in @avd.  Return %0 if all @requested permissions are granted,
  * -%EACCES if any permissions are denied, or another -errno upon
  * other errors.  This function is typically called by avc_has_perm(),
@@ -967,72 +1017,43 @@ int avc_ss_set_auditdeny(u32 ssid, u32 t
  */
 int avc_has_perm_noaudit(u32 ssid, u32 tsid,
                          u16 tclass, u32 requested,
-                         struct avc_entry_ref *aeref, struct av_decision *avd)
+                         struct av_decision *avd)
 {
-	struct avc_entry *ae;
+	struct avc_node *node;
+	struct avc_entry entry, *p_ae;
 	int rc = 0;
-	unsigned long flags;
-	struct avc_entry entry;
 	u32 denied;
-	struct avc_entry_ref ref;
 
-	if (!aeref) {
-		avc_entry_ref_init(&ref);
-		aeref = &ref;
-	}
-
-	spin_lock_irqsave(&avc_lock, flags);
+	rcu_read_lock();
 	avc_cache_stats_incr(AVC_ENTRY_LOOKUPS);
-	ae = aeref->ae;
-	if (ae) {
-		if (ae->ssid == ssid &&
-		    ae->tsid == tsid &&
-		    ae->tclass == tclass &&
-		    ((ae->avd.decided & requested) == requested)) {
-			avc_cache_stats_incr(AVC_ENTRY_HITS);
-			ae->used = 1;
-		} else {
-			avc_cache_stats_incr(AVC_ENTRY_DISCARDS);
-			ae = NULL;
-		}
-	}
 
-	if (!ae) {
-		avc_cache_stats_incr(AVC_ENTRY_MISSES);
-		rc = avc_lookup(ssid, tsid, tclass, requested, aeref);
-		if (rc) {
-			spin_unlock_irqrestore(&avc_lock,flags);
-			rc = security_compute_av(ssid,tsid,tclass,requested,&entry.avd);
-			if (rc)
-				goto out;
-			spin_lock_irqsave(&avc_lock, flags);
-			rc = avc_insert(ssid,tsid,tclass,&entry,aeref);
-			if (rc) {
-				spin_unlock_irqrestore(&avc_lock,flags);
-				goto out;
-			}
-		}
-		ae = aeref->ae;
+	node = avc_lookup(ssid, tsid, tclass, requested);
+	if (!node) {
+		rcu_read_unlock();
+		rc = security_compute_av(ssid,tsid,tclass,requested,&entry.avd);
+		if (rc)
+			goto out;
+		rcu_read_lock();
+		node = avc_insert(ssid,tsid,tclass,&entry);
 	}
 
+	p_ae = node ? &node->ae : &entry;
+
 	if (avd)
-		memcpy(avd, &ae->avd, sizeof(*avd));
+		memcpy(avd, &p_ae->avd, sizeof(*avd));
 
-	denied = requested & ~(ae->avd.allowed);
+	denied = requested & ~(p_ae->avd.allowed);
 
 	if (!requested || denied) {
-		if (selinux_enforcing) {
-			spin_unlock_irqrestore(&avc_lock,flags);
+		if (selinux_enforcing)
 			rc = -EACCES;
-			goto out;
-		} else {
-			ae->avd.allowed |= requested;
-			spin_unlock_irqrestore(&avc_lock,flags);
-			goto out;
-		}
+		else
+			if (node)
+				avc_update_node(AVC_CALLBACK_GRANT,requested,
+						ssid,tsid,tclass);
 	}
 
-	spin_unlock_irqrestore(&avc_lock,flags);
+	rcu_read_unlock();
 out:
 	return rc;
 }
@@ -1043,26 +1064,23 @@ out:
  * @tsid: target security identifier
  * @tclass: target security class
  * @requested: requested permissions, interpreted based on @tclass
- * @aeref:  AVC entry reference
  * @auditdata: auxiliary audit data
  *
  * Check the AVC to determine whether the @requested permissions are granted
  * for the SID pair (@ssid, @tsid), interpreting the permissions
  * based on @tclass, and call the security server on a cache miss to obtain
- * a new decision and add it to the cache.  Update @aeref to refer to an AVC
- * entry with the resulting decisions.  Audit the granting or denial of
+ * a new decision and add it to the cache.  Audit the granting or denial of
  * permissions in accordance with the policy.  Return %0 if all @requested
  * permissions are granted, -%EACCES if any permissions are denied, or
  * another -errno upon other errors.
  */
 int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
-                 u32 requested, struct avc_entry_ref *aeref,
-                 struct avc_audit_data *auditdata)
+                 u32 requested, struct avc_audit_data *auditdata)
 {
 	struct av_decision avd;
 	int rc;
 
-	rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, aeref, &avd);
+	rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, &avd);
 	avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
 	return rc;
 }
diff -puN security/selinux/hooks.c~selinux-scalability-convert-avc-to-rcu security/selinux/hooks.c
--- 25/security/selinux/hooks.c~selinux-scalability-convert-avc-to-rcu	2004-11-30 01:23:18.075019288 -0800
+++ 25-akpm/security/selinux/hooks.c	2004-11-30 01:23:18.094016400 -0800
@@ -448,12 +448,12 @@ static int try_context_mount(struct supe
 		}
 
 		rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
-		                  FILESYSTEM__RELABELFROM, NULL, NULL);
+		                  FILESYSTEM__RELABELFROM, NULL);
 		if (rc)
 			goto out_free;
 
 		rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
-		                  FILESYSTEM__RELABELTO, NULL, NULL);
+		                  FILESYSTEM__RELABELTO, NULL);
 		if (rc)
 			goto out_free;
 
@@ -476,12 +476,12 @@ static int try_context_mount(struct supe
 			goto out_free;
 
 		rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
-				  FILESYSTEM__RELABELFROM, NULL, NULL);
+				  FILESYSTEM__RELABELFROM, NULL);
 		if (rc)
 			goto out_free;
 
 		rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
-				  FILESYSTEM__ASSOCIATE, NULL, NULL);
+				  FILESYSTEM__ASSOCIATE, NULL);
 		if (rc)
 			goto out_free;
 
@@ -927,7 +927,7 @@ int task_has_perm(struct task_struct *ts
 	tsec1 = tsk1->security;
 	tsec2 = tsk2->security;
 	return avc_has_perm(tsec1->sid, tsec2->sid,
-			    SECCLASS_PROCESS, perms, &tsec2->avcr, NULL);
+			    SECCLASS_PROCESS, perms, NULL);
 }
 
 /* Check whether a task is allowed to use a capability. */
@@ -944,7 +944,7 @@ int task_has_capability(struct task_stru
 	ad.u.cap = cap;
 
 	return avc_has_perm(tsec->sid, tsec->sid,
-			    SECCLASS_CAPABILITY, CAP_TO_MASK(cap), NULL, &ad);
+			    SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad);
 }
 
 /* Check whether a task is allowed to use a system operation. */
@@ -956,18 +956,15 @@ int task_has_system(struct task_struct *
 	tsec = tsk->security;
 
 	return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
-			    SECCLASS_SYSTEM, perms, NULL, NULL);
+			    SECCLASS_SYSTEM, perms, NULL);
 }
 
 /* Check whether a task has a particular permission to an inode.
-   The 'aeref' parameter is optional and allows other AVC
-   entry references to be passed (e.g. the one in the struct file).
    The 'adp' parameter is optional and allows other audit
    data to be passed (e.g. the dentry). */
 int inode_has_perm(struct task_struct *tsk,
 		   struct inode *inode,
 		   u32 perms,
-		   struct avc_entry_ref *aeref,
 		   struct avc_audit_data *adp)
 {
 	struct task_security_struct *tsec;
@@ -983,8 +980,7 @@ int inode_has_perm(struct task_struct *t
 		ad.u.fs.inode = inode;
 	}
 
-	return avc_has_perm(tsec->sid, isec->sid, isec->sclass,
-			    perms, aeref ? aeref : &isec->avcr, adp);
+	return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
 }
 
 /* Same as inode_has_perm, but pass explicit audit data containing
@@ -1000,7 +996,7 @@ static inline int dentry_has_perm(struct
 	AVC_AUDIT_DATA_INIT(&ad,FS);
 	ad.u.fs.mnt = mnt;
 	ad.u.fs.dentry = dentry;
-	return inode_has_perm(tsk, inode, av, NULL, &ad);
+	return inode_has_perm(tsk, inode, av, &ad);
 }
 
 /* Check whether a task can use an open file descriptor to
@@ -1031,14 +1027,14 @@ static inline int file_has_perm(struct t
 		rc = avc_has_perm(tsec->sid, fsec->sid,
 				  SECCLASS_FD,
 				  FD__USE,
-				  &fsec->avcr, &ad);
+				  &ad);
 		if (rc)
 			return rc;
 	}
 
 	/* av is zero if only checking access to the descriptor. */
 	if (av)
-		return inode_has_perm(tsk, inode, av, &fsec->inode_avcr, &ad);
+		return inode_has_perm(tsk, inode, av, &ad);
 
 	return 0;
 }
@@ -1064,7 +1060,7 @@ static int may_create(struct inode *dir,
 
 	rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
 			  DIR__ADD_NAME | DIR__SEARCH,
-			  &dsec->avcr, &ad);
+			  &ad);
 	if (rc)
 		return rc;
 
@@ -1077,13 +1073,13 @@ static int may_create(struct inode *dir,
 			return rc;
 	}
 
-	rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, NULL, &ad);
+	rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
 	if (rc)
 		return rc;
 
 	return avc_has_perm(newsid, sbsec->sid,
 			    SECCLASS_FILESYSTEM,
-			    FILESYSTEM__ASSOCIATE, NULL, &ad);
+			    FILESYSTEM__ASSOCIATE, &ad);
 }
 
 #define MAY_LINK   0
@@ -1111,8 +1107,7 @@ static int may_link(struct inode *dir,
 
 	av = DIR__SEARCH;
 	av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
-	rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
-			  av, &dsec->avcr, &ad);
+	rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
 	if (rc)
 		return rc;
 
@@ -1131,8 +1126,7 @@ static int may_link(struct inode *dir,
 		return 0;
 	}
 
-	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
-			  av, &isec->avcr, &ad);
+	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
 	return rc;
 }
 
@@ -1158,21 +1152,16 @@ static inline int may_rename(struct inod
 
 	ad.u.fs.dentry = old_dentry;
 	rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
-			  DIR__REMOVE_NAME | DIR__SEARCH,
-			  &old_dsec->avcr, &ad);
+			  DIR__REMOVE_NAME | DIR__SEARCH, &ad);
 	if (rc)
 		return rc;
 	rc = avc_has_perm(tsec->sid, old_isec->sid,
-			  old_isec->sclass,
-			  FILE__RENAME,
-			  &old_isec->avcr, &ad);
+			  old_isec->sclass, FILE__RENAME, &ad);
 	if (rc)
 		return rc;
 	if (old_is_dir && new_dir != old_dir) {
 		rc = avc_has_perm(tsec->sid, old_isec->sid,
-				  old_isec->sclass,
-				  DIR__REPARENT,
-				  &old_isec->avcr, &ad);
+				  old_isec->sclass, DIR__REPARENT, &ad);
 		if (rc)
 			return rc;
 	}
@@ -1181,8 +1170,7 @@ static inline int may_rename(struct inod
 	av = DIR__ADD_NAME | DIR__SEARCH;
 	if (new_dentry->d_inode)
 		av |= DIR__REMOVE_NAME;
-	rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR,
-			  av,&new_dsec->avcr, &ad);
+	rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
 	if (rc)
 		return rc;
 	if (new_dentry->d_inode) {
@@ -1190,8 +1178,7 @@ static inline int may_rename(struct inod
 		new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
 		rc = avc_has_perm(tsec->sid, new_isec->sid,
 				  new_isec->sclass,
-				  (new_is_dir ? DIR__RMDIR : FILE__UNLINK),
-				  &new_isec->avcr, &ad);
+				  (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
 		if (rc)
 			return rc;
 	}
@@ -1211,7 +1198,7 @@ int superblock_has_perm(struct task_stru
 	tsec = tsk->security;
 	sbsec = sb->s_security;
 	return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
-			    perms, NULL, ad);
+			    perms, ad);
 }
 
 /* Convert a Linux mode and permission mask to an access vector. */
@@ -1448,7 +1435,7 @@ static int selinux_sysctl(ctl_table *tab
 	 * a bad coupling between this module and sysctl.c */
 	if(op == 001) {
 		error = avc_has_perm(tsec->sid, tsid,
-				     SECCLASS_DIR, DIR__SEARCH, NULL, NULL);
+				     SECCLASS_DIR, DIR__SEARCH, NULL);
 	} else {
 		av = 0;
 		if (op & 004)
@@ -1457,7 +1444,7 @@ static int selinux_sysctl(ctl_table *tab
 			av |= FILE__WRITE;
 		if (av)
 			error = avc_has_perm(tsec->sid, tsid,
-					     SECCLASS_FILE, av, NULL, NULL);
+					     SECCLASS_FILE, av, NULL);
         }
 
 	return error;
@@ -1576,8 +1563,7 @@ static int selinux_vm_enough_memory(long
 		if (!rc) {
 			rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
 						  SECCLASS_CAPABILITY,
-						  CAP_TO_MASK(CAP_SYS_ADMIN),
-						  NULL, NULL);
+						  CAP_TO_MASK(CAP_SYS_ADMIN), NULL);
 		}
 		if (rc)
 			free -= free / 32;
@@ -1669,22 +1655,18 @@ static int selinux_bprm_set_security(str
 
         if (tsec->sid == newsid) {
 		rc = avc_has_perm(tsec->sid, isec->sid,
-				  SECCLASS_FILE, FILE__EXECUTE_NO_TRANS,
-				  &isec->avcr, &ad);
+				  SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
 		if (rc)
 			return rc;
 	} else {
 		/* Check permissions for the transition. */
 		rc = avc_has_perm(tsec->sid, newsid,
-				  SECCLASS_PROCESS, PROCESS__TRANSITION,
-				  NULL,
-				  &ad);
+				  SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
 		if (rc)
 			return rc;
 
 		rc = avc_has_perm(newsid, isec->sid,
-				  SECCLASS_FILE, FILE__ENTRYPOINT,
-				  &isec->avcr, &ad);
+				  SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
 		if (rc)
 			return rc;
 
@@ -1716,7 +1698,7 @@ static int selinux_bprm_secureexec (stru
 		   the two SIDs, i.e. ahp returns 0. */
 		atsecure = avc_has_perm(tsec->osid, tsec->sid,
 					 SECCLASS_PROCESS,
-					 PROCESS__NOATSECURE, NULL, NULL);
+					 PROCESS__NOATSECURE, NULL);
 	}
 
 	return (atsecure || secondary_ops->bprm_secureexec(bprm));
@@ -1751,8 +1733,7 @@ static inline void flush_unauthorized_fi
 			   interested in the inode-based check here. */
 			struct inode *inode = file->f_dentry->d_inode;
 			if (inode_has_perm(current, inode,
-					   FILE__READ | FILE__WRITE,
-					   NULL, NULL)) {
+					   FILE__READ | FILE__WRITE, NULL)) {
 				/* Reset controlling tty. */
 				current->signal->tty = NULL;
 				current->signal->tty_old_pgrp = 0;
@@ -1838,8 +1819,7 @@ static void selinux_bprm_apply_creds(str
 		   unchanged and kill. */
 		if (unsafe & LSM_UNSAFE_SHARE) {
 			rc = avc_has_perm_noaudit(tsec->sid, sid,
-					  SECCLASS_PROCESS, PROCESS__SHARE,
-					  NULL, &avd);
+					  SECCLASS_PROCESS, PROCESS__SHARE, &avd);
 			if (rc) {
 				task_unlock(current);
 				avc_audit(tsec->sid, sid, SECCLASS_PROCESS,
@@ -1853,8 +1833,7 @@ static void selinux_bprm_apply_creds(str
 		   Otherwise, leave SID unchanged and kill. */
 		if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
 			rc = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
-					  SECCLASS_PROCESS, PROCESS__PTRACE,
-					  NULL, &avd);
+					  SECCLASS_PROCESS, PROCESS__PTRACE, &avd);
 			if (!rc)
 				tsec->sid = sid;
 			task_unlock(current);
@@ -1879,7 +1858,7 @@ static void selinux_bprm_apply_creds(str
                   been updated so that any kill done after the flush
                   will be checked against the new SID. */
 		rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
-				  PROCESS__SIGINH, NULL, NULL);
+				  PROCESS__SIGINH, NULL);
 		if (rc) {
 			memset(&itimer, 0, sizeof itimer);
 			for (i = 0; i < 3; i++)
@@ -1903,7 +1882,7 @@ static void selinux_bprm_apply_creds(str
 		   is lower than the hard limit, e.g. RLIMIT_CORE or 
 		   RLIMIT_STACK.*/
 		rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
-				  PROCESS__RLIMITINH, NULL, NULL);
+				  PROCESS__RLIMITINH, NULL);
 		if (rc) {
 			for (i = 0; i < RLIM_NLIMITS; i++) {
 				rlim = current->signal->rlim + i;
@@ -2189,7 +2168,7 @@ static int selinux_inode_permission(stru
 	}
 
 	return inode_has_perm(current, inode,
-			       file_mask_to_av(inode->i_mode, mask), NULL, NULL);
+			       file_mask_to_av(inode->i_mode, mask), NULL);
 }
 
 static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
@@ -2247,8 +2226,7 @@ static int selinux_inode_setxattr(struct
 	ad.u.fs.dentry = dentry;
 
 	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
-			  FILE__RELABELFROM,
-			  &isec->avcr, &ad);
+			  FILE__RELABELFROM, &ad);
 	if (rc)
 		return rc;
 
@@ -2257,7 +2235,7 @@ static int selinux_inode_setxattr(struct
 		return rc;
 
 	rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
-			  FILE__RELABELTO, NULL, &ad);
+			  FILE__RELABELTO, &ad);
 	if (rc)
 		return rc;
 
@@ -2265,7 +2243,6 @@ static int selinux_inode_setxattr(struct
 			    sbsec->sid,
 			    SECCLASS_FILESYSTEM,
 			    FILESYSTEM__ASSOCIATE,
-			    NULL,
 			    &ad);
 }
 
@@ -2587,7 +2564,7 @@ static int selinux_file_send_sigiotask(s
 		perm = signal_to_av(signum);
 
 	return avc_has_perm(fsec->fown_sid, tsec->sid,
-			    SECCLASS_PROCESS, perm, NULL, NULL);
+			    SECCLASS_PROCESS, perm, NULL);
 }
 
 static int selinux_file_receive(struct file *file)
@@ -2724,8 +2701,7 @@ static int selinux_task_setscheduler(str
 	   is held and the system will deadlock if we try to log an audit
 	   message. */
 	return avc_has_perm_noaudit(tsec1->sid, tsec2->sid,
-				    SECCLASS_PROCESS, PROCESS__SETSCHED,
-				    &tsec2->avcr, NULL);
+				    SECCLASS_PROCESS, PROCESS__SETSCHED, NULL);
 }
 
 static int selinux_task_getscheduler(struct task_struct *p)
@@ -2968,8 +2944,7 @@ static int socket_has_perm(struct task_s
 
 	AVC_AUDIT_DATA_INIT(&ad,NET);
 	ad.u.net.sk = sock->sk;
-	err = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
-			   perms, &isec->avcr, &ad);
+	err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
 
 out:
 	return err;
@@ -2987,7 +2962,7 @@ static int selinux_socket_create(int fam
 	tsec = current->security;
 	err = avc_has_perm(tsec->sid, tsec->sid,
 			   socket_type_to_security_class(family, type,
-			   protocol), SOCKET__CREATE, NULL, NULL);
+			   protocol), SOCKET__CREATE, NULL);
 
 out:
 	return err;
@@ -3068,7 +3043,7 @@ static int selinux_socket_bind(struct so
 			ad.u.net.family = family;
 			err = avc_has_perm(isec->sid, sid,
 					   isec->sclass,
-					   SOCKET__NAME_BIND, NULL, &ad);
+					   SOCKET__NAME_BIND, &ad);
 			if (err)
 				goto out;
 		}
@@ -3101,7 +3076,7 @@ static int selinux_socket_bind(struct so
 			ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr);
 
 		err = avc_has_perm(isec->sid, sid,
-		                   isec->sclass, node_perm, NULL, &ad);
+		                   isec->sclass, node_perm, &ad);
 		if (err)
 			goto out;
 	}
@@ -3201,8 +3176,7 @@ static int selinux_socket_unix_stream_co
 
 	err = avc_has_perm(isec->sid, other_isec->sid,
 			   isec->sclass,
-			   UNIX_STREAM_SOCKET__CONNECTTO,
-			   &other_isec->avcr, &ad);
+			   UNIX_STREAM_SOCKET__CONNECTTO, &ad);
 	if (err)
 		return err;
 
@@ -3232,9 +3206,7 @@ static int selinux_socket_unix_may_send(
 	ad.u.net.sk = other->sk;
 
 	err = avc_has_perm(isec->sid, other_isec->sid,
-			   isec->sclass,
-			   SOCKET__SENDTO,
-			   &other_isec->avcr, &ad);
+			   isec->sclass, SOCKET__SENDTO, &ad);
 	if (err)
 		return err;
 
@@ -3312,8 +3284,7 @@ static int selinux_socket_sock_rcv_skb(s
 	if (err)
 		goto out;
 
-	err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF,
-	                   netif_perm, NULL, &ad);
+	err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, &ad);
 	if (err)
 		goto out;
 	
@@ -3322,7 +3293,7 @@ static int selinux_socket_sock_rcv_skb(s
 	if (err)
 		goto out;
 	
-	err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, NULL, &ad);
+	err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, &ad);
 	if (err)
 		goto out;
 
@@ -3336,8 +3307,8 @@ static int selinux_socket_sock_rcv_skb(s
 		if (err)
 			goto out;
 
-		err = avc_has_perm(sock_sid, port_sid, sock_class,
-		                   recv_perm, NULL, &ad);
+		err = avc_has_perm(sock_sid, port_sid,
+				   sock_class, recv_perm, &ad);
 	}
 out:	
 	return err;
@@ -3486,7 +3457,7 @@ static unsigned int selinux_ip_postroute
 		goto out;
 
 	err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF,
-	                   netif_perm, NULL, &ad) ? NF_DROP : NF_ACCEPT;
+	                   netif_perm, &ad) ? NF_DROP : NF_ACCEPT;
 	if (err != NF_ACCEPT)
 		goto out;
 		
@@ -3497,7 +3468,7 @@ static unsigned int selinux_ip_postroute
 		goto out;
 	
 	err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE,
-	                   node_perm, NULL, &ad) ? NF_DROP : NF_ACCEPT;
+	                   node_perm, &ad) ? NF_DROP : NF_ACCEPT;
 	if (err != NF_ACCEPT)
 		goto out;
 
@@ -3514,7 +3485,7 @@ static unsigned int selinux_ip_postroute
 			goto out;
 
 		err = avc_has_perm(isec->sid, port_sid, isec->sclass,
-		                   send_perm, NULL, &ad) ? NF_DROP : NF_ACCEPT;
+		                   send_perm, &ad) ? NF_DROP : NF_ACCEPT;
 	}
 
 out:
@@ -3651,8 +3622,7 @@ static int ipc_has_perm(struct kern_ipc_
 	AVC_AUDIT_DATA_INIT(&ad, IPC);
 	ad.u.ipc_id = ipc_perms->key;
 
-	return avc_has_perm(tsec->sid, isec->sid, sclass,
-			    perms, &isec->avcr, &ad);
+	return avc_has_perm(tsec->sid, isec->sid, sclass, perms, &ad);
 }
 
 static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
@@ -3684,7 +3654,7 @@ static int selinux_msg_queue_alloc_secur
  	ad.u.ipc_id = msq->q_perm.key;
 
 	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
-			  MSGQ__CREATE, &isec->avcr, &ad);
+			  MSGQ__CREATE, &ad);
 	if (rc) {
 		ipc_free_security(&msq->q_perm);
 		return rc;
@@ -3710,7 +3680,7 @@ static int selinux_msg_queue_associate(s
 	ad.u.ipc_id = msq->q_perm.key;
 
 	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
-			    MSGQ__ASSOCIATE, &isec->avcr, &ad);
+			    MSGQ__ASSOCIATE, &ad);
 }
 
 static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
@@ -3774,17 +3744,15 @@ static int selinux_msg_queue_msgsnd(stru
 
 	/* Can this process write to the queue? */
 	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
-			  MSGQ__WRITE, &isec->avcr, &ad);
+			  MSGQ__WRITE, &ad);
 	if (!rc)
 		/* Can this process send the message */
 		rc = avc_has_perm(tsec->sid, msec->sid,
-				  SECCLASS_MSG, MSG__SEND,
-				  &msec->avcr, &ad);
+				  SECCLASS_MSG, MSG__SEND, &ad);
 	if (!rc)
 		/* Can the message be put in the queue? */
 		rc = avc_has_perm(msec->sid, isec->sid,
-				  SECCLASS_MSGQ, MSGQ__ENQUEUE,
-				  &isec->avcr, &ad);
+				  SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad);
 
 	return rc;
 }
@@ -3807,12 +3775,10 @@ static int selinux_msg_queue_msgrcv(stru
  	ad.u.ipc_id = msq->q_perm.key;
 
 	rc = avc_has_perm(tsec->sid, isec->sid,
-			  SECCLASS_MSGQ, MSGQ__READ,
-			  &isec->avcr, &ad);
+			  SECCLASS_MSGQ, MSGQ__READ, &ad);
 	if (!rc)
 		rc = avc_has_perm(tsec->sid, msec->sid,
-				  SECCLASS_MSG, MSG__RECEIVE,
-				  &msec->avcr, &ad);
+				  SECCLASS_MSG, MSG__RECEIVE, &ad);
 	return rc;
 }
 
@@ -3835,7 +3801,7 @@ static int selinux_shm_alloc_security(st
  	ad.u.ipc_id = shp->shm_perm.key;
 
 	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
-			  SHM__CREATE, &isec->avcr, &ad);
+			  SHM__CREATE, &ad);
 	if (rc) {
 		ipc_free_security(&shp->shm_perm);
 		return rc;
@@ -3861,7 +3827,7 @@ static int selinux_shm_associate(struct 
 	ad.u.ipc_id = shp->shm_perm.key;
 
 	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
-			    SHM__ASSOCIATE, &isec->avcr, &ad);
+			    SHM__ASSOCIATE, &ad);
 }
 
 /* Note, at this point, shp is locked down */
@@ -3934,7 +3900,7 @@ static int selinux_sem_alloc_security(st
  	ad.u.ipc_id = sma->sem_perm.key;
 
 	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
-			  SEM__CREATE, &isec->avcr, &ad);
+			  SEM__CREATE, &ad);
 	if (rc) {
 		ipc_free_security(&sma->sem_perm);
 		return rc;
@@ -3960,7 +3926,7 @@ static int selinux_sem_associate(struct 
 	ad.u.ipc_id = sma->sem_perm.key;
 
 	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
-			    SEM__ASSOCIATE, &isec->avcr, &ad);
+			    SEM__ASSOCIATE, &ad);
 }
 
 /* Note, at this point, sma is locked down */
diff -puN security/selinux/include/avc.h~selinux-scalability-convert-avc-to-rcu security/selinux/include/avc.h
--- 25/security/selinux/include/avc.h~selinux-scalability-convert-avc-to-rcu	2004-11-30 01:23:18.076019136 -0800
+++ 25-akpm/security/selinux/include/avc.h	2004-11-30 01:23:18.094016400 -0800
@@ -29,19 +29,6 @@ extern int selinux_enforcing;
  */
 struct avc_entry;
 
-/*
- * A reference to an AVC entry.
- */
-struct avc_entry_ref {
-	struct avc_entry *ae;
-};
-
-/* Initialize an AVC entry reference before first use. */
-static inline void avc_entry_ref_init(struct avc_entry_ref *h)
-{
-	h->ae = NULL;
-}
-
 struct task_struct;
 struct vfsmount;
 struct dentry;
@@ -118,23 +105,17 @@ void avc_dump_query(struct audit_buffer 
 
 void __init avc_init(void);
 
-int avc_lookup(u32 ssid, u32 tsid, u16 tclass,
-               u32 requested, struct avc_entry_ref *aeref);
-
-int avc_insert(u32 ssid, u32 tsid, u16 tclass,
-               struct avc_entry *ae, struct avc_entry_ref *out_aeref);
-
 void avc_audit(u32 ssid, u32 tsid,
                u16 tclass, u32 requested,
                struct av_decision *avd, int result, struct avc_audit_data *auditdata);
 
 int avc_has_perm_noaudit(u32 ssid, u32 tsid,
                          u16 tclass, u32 requested,
-                         struct avc_entry_ref *aeref, struct av_decision *avd);
+                         struct av_decision *avd);
 
 int avc_has_perm(u32 ssid, u32 tsid,
                  u16 tclass, u32 requested,
-                 struct avc_entry_ref *aeref, struct avc_audit_data *auditdata);
+                 struct avc_audit_data *auditdata);
 
 #define AVC_CALLBACK_GRANT		1
 #define AVC_CALLBACK_TRY_REVOKE		2
diff -puN security/selinux/include/objsec.h~selinux-scalability-convert-avc-to-rcu security/selinux/include/objsec.h
--- 25/security/selinux/include/objsec.h~selinux-scalability-convert-avc-to-rcu	2004-11-30 01:23:18.078018832 -0800
+++ 25-akpm/security/selinux/include/objsec.h	2004-11-30 01:23:18.095016248 -0800
@@ -33,7 +33,6 @@ struct task_security_struct {
 	u32 sid;             /* current SID */
 	u32 exec_sid;        /* exec SID */
 	u32 create_sid;      /* fscreate SID */
-        struct avc_entry_ref avcr;     /* reference to process permissions */
 	u32 ptrace_sid;      /* SID of ptrace parent */
 };
 
@@ -44,7 +43,6 @@ struct inode_security_struct {
 	u32 task_sid;        /* SID of creating task */
 	u32 sid;             /* SID of this object */
 	u16 sclass;       /* security class of this object */
-	struct avc_entry_ref avcr;     /* reference to object permissions */
 	unsigned char initialized;     /* initialization flag */
 	struct semaphore sem;
 	unsigned char inherit;         /* inherit SID from parent entry */
@@ -55,8 +53,6 @@ struct file_security_struct {
 	struct file *file;              /* back pointer to file object */
 	u32 sid;              /* SID of open file description */
 	u32 fown_sid;         /* SID of file owner (for SIGIO) */
-	struct avc_entry_ref avcr;	/* reference to fd permissions */
-	struct avc_entry_ref inode_avcr;     /* reference to object permissions */
 };
 
 struct superblock_security_struct {
@@ -77,7 +73,6 @@ struct msg_security_struct {
         unsigned long magic;		/* magic number for this module */
 	struct msg_msg *msg;		/* back pointer */
 	u32 sid;              /* SID of message */
-        struct avc_entry_ref avcr;	/* reference to permissions */
 };
 
 struct ipc_security_struct {
@@ -85,7 +80,6 @@ struct ipc_security_struct {
 	struct kern_ipc_perm *ipc_perm; /* back pointer */
 	u16 sclass;	/* security class of this object */
 	u32 sid;              /* SID of IPC resource */
-        struct avc_entry_ref avcr;	/* reference to permissions */
 };
 
 struct bprm_security_struct {
diff -puN security/selinux/selinuxfs.c~selinux-scalability-convert-avc-to-rcu security/selinux/selinuxfs.c
--- 25/security/selinux/selinuxfs.c~selinux-scalability-convert-avc-to-rcu	2004-11-30 01:23:18.080018528 -0800
+++ 25-akpm/security/selinux/selinuxfs.c	2004-11-30 01:23:18.096016096 -0800
@@ -51,7 +51,7 @@ int task_has_security(struct task_struct
 		return -EACCES;
 
 	return avc_has_perm(tsec->sid, SECINITSID_SECURITY,
-			    SECCLASS_SECURITY, perms, NULL, NULL);
+			    SECCLASS_SECURITY, perms, NULL);
 }
 
 enum sel_inos {
_
