[SSI] openssi/kernel/cluster/ssi/token clitok.c, 1.6, 1.7 mhsvrtok.c, 1.6, 1.7 msgsup.c, 1.6, 1.7 rsvrtok.c, 1.6, 1.7 svrtok.c, 1.5, 1.6

Roger Tsang <[email protected]>
Newsgroups gmane.linux.cluster.ssic.cvs
Message-ID <[email protected]>
Update of /cvsroot/ssic-linux/openssi/kernel/cluster/ssi/token
In directory fdv4jf1.ch3.sourceforge.com:/tmp/cvs-serv8553/cluster/ssi/token

Modified Files:
      Tag: OPENSSI-FC
	clitok.c mhsvrtok.c msgsup.c rsvrtok.c svrtok.c 
Log Message:
Bug fixes and enhancements. (see ChangeLog)


Index: rsvrtok.c
===================================================================
RCS file: /cvsroot/ssic-linux/openssi/kernel/cluster/ssi/token/rsvrtok.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- rsvrtok.c	21 Oct 2004 23:40:49 -0000	1.6
+++ rsvrtok.c	3 Feb 2009 06:18:12 -0000	1.7
@@ -293,10 +293,13 @@
 		contextp = svrglue_alloc_ctx();
 
 	gluectxp = (tokctx_mod_t *)contextp;
+	mb();
 	contextp += svr_top_glue_ctx_size;
 	rctxp = (tokctx_mod_t *)contextp;
+	mb();
 	contextp += tnc_rtok_ctx_size;
 	nrctxp = (tokctx_mod_t *)contextp;
+	mb();
 	contextp += tns_nrtok_ctx_size;
 
 	svrglue_init_ctx(gluectxp,
@@ -586,6 +589,7 @@
 	/* If start earlier than requested start adjust start,len */
 	if (qret->start < start) {
 		qret->len -= (start - qret->start);
+		mb();
 		qret->start = start;
 	}
 	/* If end later than requested adjust length */
@@ -1075,6 +1079,7 @@
 		 * has no bearing on split ranges from server to client.
 		 */
 		save_op_range = rng_ctx->tc_op_range;
+		mb();
 		rng_ctx->tc_op_range = rng_ctx->tc_curchunk->rc_range;
 
 		nr_ret = svrtok_clear(

Index: svrtok.c
===================================================================
RCS file: /cvsroot/ssic-linux/openssi/kernel/cluster/ssi/token/svrtok.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- svrtok.c	21 Oct 2004 23:40:49 -0000	1.5
+++ svrtok.c	3 Feb 2009 06:18:13 -0000	1.6
@@ -110,44 +110,74 @@
 	/* NOTREACHED */
 }
 
+#ifdef NRSVRFIFO_LINUX_LIST
+static inline void
+#else
 STATIC void
-svrinitfifo(struct nrsvrfifo *fifop)
+#endif
+svrinitfifo(nrsvrfifo_t *fifop)
 {
[...1041 lines suppressed...]
 }
 #endif	/* !__KERNEL__ || DEBUG || DEBUG_TOOLS */
 
+#ifndef SVRTOK_KMEM_CACHE
 int
 svrtok_nd_free_memory(int count, unsigned int gfp_mask)
 {
@@ -2281,6 +2627,7 @@
 			nrwant_free_count -= chunk;
 			nrwant_free = nrwantp;
 			UNLOCK_SPIN_LOCK(&nrwant_free_lock);
+			mb();
 			nrwantp = startp;
 			for (; chunk > 0; chunk--) {
 				nextp = nrwantp->wan_next;
@@ -2295,3 +2642,4 @@
 
 	return nrwant_free_count;
 }
+#endif /* !SVRTOK_KMEM_CACHE */

Index: msgsup.c
===================================================================
RCS file: /cvsroot/ssic-linux/openssi/kernel/cluster/ssi/token/msgsup.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- msgsup.c	10 Oct 2008 08:10:32 -0000	1.6
+++ msgsup.c	3 Feb 2009 06:18:12 -0000	1.7
@@ -115,6 +115,10 @@
 
 #ifdef __KERNEL__
 struct msg {
+#ifdef RCU_MSGSUP
+	struct list_head msg_list;
+	struct rcu_head msg_rcu;
+#endif
 	object_t obj;
 	int op;
 	range_off_t start;
@@ -127,28 +131,45 @@
 #ifdef DEBUG
 	pid_t	pid;
 #endif /* DEBUG */
+#ifndef RCU_MSGSUP
 	struct msg *next;
 	struct msg *back;
+#endif
 };
 
 struct id {
+#ifdef RCU_MSGSUP
+	struct list_head id_list;
+	struct rcu_head id_rcu;
+#endif
 	long	id;
+#ifndef RCU_MSGSUP
 	struct id *next;
+#endif
 #ifdef DEBUG
 	pid_t	pid;
 #endif
 };
-#endif
 
-#if	defined(__KERNEL__)
-STATIC struct msg *msgsup_head = NULL;
+#ifdef RCU_MSGSUP
+STATIC kmem_cache_t *msgsup_cachep;
+STATIC kmem_cache_t *msgsup_id_cachep;
+STATIC LIST_HEAD(msgsup_head);
+STATIC LIST_HEAD(msgsup_idlist);
+#ifdef DEBUG
 STATIC int msgsup_head_count = 0;
-STATIC struct id *msgsup_idlist = NULL;
+#endif
+MSG_LOCK_T(msgsup_idlist_lock);
+#else /* !RCU_MSGSUP */
+STATIC struct msg *msgsup_head = NULL;
 STATIC struct msg *msg_free = NULL;
-STATIC int msg_free_count = 0;
-STATIC MSG_LOCK_T(msg_lock);
+STATIC struct id *msgsup_idlist = NULL;
 STATIC struct id *msg_free_idlist = NULL;
+STATIC int msg_free_count = 0;
 STATIC int msg_free_idlist_count = 0;
+STATIC int msgsup_head_count = 0;
+#endif /* !RCU_MSGSUP */
+STATIC MSG_LOCK_T(msg_lock);
 #endif	/* __KERNEL__  */
 
 #ifdef DEBUG
@@ -165,11 +186,22 @@
 {
 	struct id *ident;
 
+#ifdef RCU_MSGSUP
+	rcu_read_lock();
+	list_for_each_entry_rcu(ident, &msgsup_idlist, id_list) {
+		if (ident->id != id)
+			continue;
+		rcu_read_unlock();
+		return TRUE;
+	}
+	rcu_read_unlock();
+#else
 	for (ident = msgsup_idlist; ident ; ident = ident->next) {
 		if (ident->id == id) {
 			return TRUE;
 		}
 	}
+#endif
 
 	return FALSE;
 }
@@ -181,6 +213,20 @@
 STATIC void
 msgsup_addid(long id)
 {
+#ifdef RCU_MSGSUP
+	struct id *newid;
+
+	newid = kmem_cache_alloc(msgsup_id_cachep, GFP_KERNEL|__GFP_NOFAIL);
+	INIT_LIST_HEAD(&newid->id_list);
+	INIT_RCU_HEAD(&newid->id_rcu);
+	newid->id = id;
+#ifdef DEBUG
+	newid->pid = NSC_MY_PID;
+#endif
+	MSG_LOCK(msgsup_idlist_lock);
+	list_add_rcu(&newid->id_list, &msgsup_idlist);
+	MSG_UNLOCK(msgsup_idlist_lock);
+#else /* !RCU_MSGSUP */
 	struct id *newid;
 
 	if ((newid = msg_free_idlist)) {
@@ -200,12 +246,20 @@
 	newid->pid = NSC_MY_PID;
 #endif
 	newid->next = msgsup_idlist;
-#ifdef TOKEN_MSGSUP_RACE_FIX
-	mb();
-#endif
 	msgsup_idlist = newid;
+#endif /* !RCU_MSGSUP */
 }
 
+#ifdef RCU_MSGSUP
+void
+msgsup_id_free(struct rcu_head *id_rcu)
+{
+	struct id *ident = container_of(id_rcu, struct id, id_rcu);
+
+	kmem_cache_free(msgsup_id_cachep, ident);
+}
+#endif /* RCU_MSGSUP */
+
 /*
  * Remove an id from list.
  * Called/Returns with MSG_LOCK() held.
@@ -213,28 +267,40 @@
 STATIC void
 msgsup_delid(long id)
 {
-	struct id **identp;
 	struct id *ident;
+#ifdef RCU_MSGSUP
+	struct list_head *pos, *n;
+
+	rcu_read_lock();
+	list_for_each_safe_rcu(pos, n, &msgsup_idlist) {
+		ident = list_entry(pos, typeof(*ident), id_list);
+		if (ident->id != id)
+			continue;
+		rcu_read_unlock();
+		MSG_LOCK(msgsup_idlist_lock);
+		list_del_rcu(&ident->id_list);
+		MSG_UNLOCK(msgsup_idlist_lock);
+		call_rcu(&ident->id_rcu, msgsup_id_free);
+		return;
+	}
+	rcu_read_unlock();
+#else
+	struct id **identp;
 
 	for (identp = &msgsup_idlist;*identp; identp = &((*identp)->next)) {
 		if ((*identp)->id == id) {
 			ident = *identp;
-#ifdef TOKEN_MSGSUP_RACE_FIX
-			mb();
-#endif
 			/* delete entry */
 			*identp = (*identp)->next;
 
 			ident->next = msg_free_idlist;
-#ifdef TOKEN_MSGSUP_RACE_FIX
-			mb();
-#endif
 			msg_free_idlist = ident;
 			msg_free_idlist_count++;
 
 			return;
 		}
 	}
+#endif /* !RCU_MSGSUP */
 
 	panic("msgsup_delid: entry not found");
 }
@@ -243,6 +309,17 @@
 msgsup_init()
 {
 	MSG_INITLOCK(msg_lock);
+#ifdef RCU_MSGSUP
+	MSG_INITLOCK(msgsup_idlist_lock);
+	msgsup_cachep = kmem_cache_create("msgsup_msg_cache",
+				sizeof(struct msg), 0,
+				SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_PANIC,
+				NULL, NULL);
+	msgsup_id_cachep = kmem_cache_create("msgsup_id_cache",
+				sizeof(struct id), 0,
+				SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_PANIC,
+				NULL, NULL);
+#endif
 }
 #endif /* __KERNEL__ */
 
@@ -261,6 +338,11 @@
 
 	SSI_ASSERT(op != HINT);
 
+#ifdef RCU_MSGSUP
+	msgp = kmem_cache_alloc(msgsup_cachep, GFP_KERNEL|__GFP_NOFAIL);
+	INIT_LIST_HEAD(&msgp->msg_list);
+	INIT_RCU_HEAD(&msgp->msg_rcu);
+#else /* !RCU_MSGSUP */
 	MSG_LOCK(msg_lock);
 
 	if ((msgp = msg_free)) {
@@ -275,6 +357,7 @@
 		}
 		MSG_LOCK(msg_lock);
 	}
+#endif /* !RCU_MSGSUP */
 
 	msgp->obj = *objp;	/* Copy object_t structure */
 	/* Call the hold_obj function if one is provided for the object */
@@ -307,13 +390,19 @@
 #endif
 
 	/* Add new messages to back of list */
+#ifdef RCU_MSGSUP
+	MSG_LOCK(msg_lock);
+	list_add_tail_rcu(&msgp->msg_list, &msgsup_head);
+#ifdef DEBUG
+	msgsup_head_count++;
+	SSI_ASSERT(msgsup_head_count < 10000);
+#endif
+	MSG_UNLOCK(msg_lock);
+#else /* !RCU_MSGSUP */
 	if (msgsup_head) {
 		msgp->next = msgsup_head;
 		msgp->back = msgsup_head->back;
 		msgsup_head->back->next = msgp;
-#ifdef TOKEN_MSGSUP_RACE_FIX
-		smp_wmb();
-#endif
 		msgsup_head->back = msgp;
 	} else {
 		msgp->next = msgp;
@@ -324,6 +413,7 @@
 	SSI_ASSERT(msgsup_head_count < 10000);
 
 	MSG_UNLOCK(msg_lock);
+#endif /* !RCU_MSGSUP */
 #endif /* __KERNEL__ */
 #if	defined(SIMUL)
 	if (op & CFSCLI) {
@@ -338,18 +428,41 @@
 }
 
 #if	defined(__KERNEL__)
+#ifdef RCU_MSGSUP
+void
+msgsup_free(struct rcu_head *msg_rcu)
+{
+	struct msg *msgp = container_of(msg_rcu, struct msg, msg_rcu);
+
+	kmem_cache_free(msgsup_cachep, msgp);
+}
+#endif /* RCU_MSGSUP */
+
 void
 process_msgs(int force)
 {
+#ifdef RCU_MSGSUP
+	struct list_head *pos, *n;
+#endif
 	struct msg *save;
-	int recurse = FALSE;
+	int recurse;
 
 	/* PERF: Optimize NOOP case */
+#ifdef RCU_MSGSUP
+	if (list_empty(&msgsup_head))
+		return;
+
+	recurse = msgsup_findid(NSC_UNIQUE_ID);
+	if (recurse) {
+		if (!force)
+			return;
+#else /* !RCU_MSGSUP */
 	if (msgsup_head == NULL)
 		return;
 
 	MSG_LOCK(msg_lock);
 
+	recurse = FALSE;
 	if (msgsup_findid(NSC_UNIQUE_ID)) {
 		if (!force) {
 			MSG_UNLOCK(msg_lock);
@@ -357,16 +470,47 @@
 		}
 
 		recurse = TRUE;
+#endif /* !RCU_MSGSUP */
 #ifdef DEBUG
 		printk(KERN_WARNING
 		    "process_msgs: forced recursive processing\n");
 #endif
-	}
-
-	if (!recurse)
+	} else
 		msgsup_addid(NSC_UNIQUE_ID);
 
 restart:
+#ifdef RCU_MSGSUP
+	rcu_read_lock();
+	list_for_each_safe_rcu(pos, n, &msgsup_head) {
+		save = list_entry(pos, typeof(*save), msg_list);
+		if (save->id != NSC_UNIQUE_ID)
+			continue;
+		MSG_LOCK(msg_lock);
+#ifdef SSI_SKIP
+		/* Linux: No need to check. See NSC_UNIQUE_ID */
+		if (save->id == 0) {
+			MSG_UNLOCK(msg_lock);
+			goto restart;
+		}
+		save->id = 0;
+#endif
+		list_del_rcu(&save->msg_list);
+#ifdef DEBUG
+		msgsup_head_count--;
+		SSI_ASSERT(msgsup_head_count >= 0);
+#endif
+		MSG_UNLOCK(msg_lock);
+		rcu_read_unlock();
+
+		TRANSPORT((&(save->obj)))(&(save->obj), save->seq,
+			save->op, save->oldmode, save->newmode, save->agent,
+			save->start, save->len);
+
+		call_rcu(&save->msg_rcu, msgsup_free);
+		goto restart;
+	}
+	rcu_read_unlock();
+#else /* !RCU_MSGSUP */
 	for (save = msgsup_head ; save ; save = save->next) {
 
 		if (save->id != NSC_UNIQUE_ID) {
@@ -403,22 +547,23 @@
 		MSG_LOCK(msg_lock);
 
 		save->next = msg_free;
-#ifdef TOKEN_MSGSUP_RACE_FIX
-		mb();
-#endif
 		msg_free = save;
 		msg_free_count++;
 
 		goto restart;
 	}
+#endif /* !RCU_MSGSUP */
 
 	if (!recurse)
 		msgsup_delid(NSC_UNIQUE_ID);
 
+#ifndef RCU_MSGSUP
 	MSG_UNLOCK(msg_lock);
+#endif
 }
 #endif	/* __KERNEL__ */
 
+#ifndef RCU_MSGSUP
 int
 msgsup_msg_free_memory(int count, unsigned int gfp_mask)
 {
@@ -489,3 +634,4 @@
 	}
 	return msg_free_idlist_count;
 }
+#endif /* !RCU_MSGSUP */

Index: clitok.c
===================================================================
RCS file: /cvsroot/ssic-linux/openssi/kernel/cluster/ssi/token/clitok.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- clitok.c	10 Oct 2008 08:10:32 -0000	1.6
+++ clitok.c	3 Feb 2009 06:18:12 -0000	1.7
@@ -74,12 +74,16 @@
 #define	printf(a)
 #endif
 
-STATIC void initfifo(struct fifo *fifop);
+STATIC void initfifo(fifo_t *fifop);
 int check_req(object_t *objp, nrtcb_t *tcbp, int mode);
 STATIC int get_hold(object_t *objp, struct nrreq *request, int flags);
 STATIC int revoke_internal(object_t *objp, struct nrrevoke *revp, int direct);
 STATIC void complete_req(object_t *objp, struct nrreq *);
+#ifdef CFSTOK_INTR
+int tok_wait(void *cookie);
+#else
 void tok_wait(nrtcb_t *tcbp, void *cookie);
+#endif
 STATIC int check_revok(object_t *, nrtcb_t *, int , int);
 
 #ifdef SIMUL
@@ -127,17 +131,26 @@
 	once on the first line of fifo output
  */
 STATIC void
-printfifo(object_t *objp, char *indent, char *msg, struct fifo *fifo)
+printfifo(object_t *objp, char *indent, char *msg, fifo_t *fifo)
 {
 	struct nrreq *reqp;
 
+#ifdef TOKEN_FIFO_LINUX_LIST
+	printk(KERN_DEBUG "%s%s begin %p end %p\n", indent, msg,
+		list_entry(fifo->next, struct nrreq, req_list),
+		list_entry(fifo->prev, struct nrreq, req_list));
+
+	list_for_each_entry(reqp, fifo, req_list)
+		printk(KERN_DEBUG "\t%s REQ mode %s objp 0x%p\n", indent,
+			MSTR(objp, reqp->req_mode), reqp->req_objp);
+#else
 	printk(KERN_DEBUG "%s%s begin %s end %s\n", indent, msg,
 	    ptr(fifo->fifo_req_begin), ptr(fifo->fifo_req_end));
 
 	for (reqp = fifo->fifo_req_begin;reqp;reqp= reqp->req_next)
 		printk(KERN_DEBUG "\t%s REQ mode %s objp 0x%p\n", indent,
 			MSTR(objp, reqp->req_mode), reqp->req_objp);
-
+#endif
 }
 
 
@@ -153,10 +166,20 @@
 	printk(KERN_DEBUG "TCB 0x%p", tcbp);
 	printk(KERN_DEBUG "\t");
 	printk(KERN_DEBUG "%s %d", MSTR(objp,tcbp->tok_mode), tcbp->tok_hld_count);
+#ifdef TOKEN_FIFO_LINUX_LIST
+	if (_tok_request(tcbp)) {
+		struct nrreq *req = tok_request(tcbp);
+
+		printk(KERN_DEBUG " req: %s%s",
+			MSTR(objp, req->req_mode),
+			(((&req->req_list)->next != &tcbp->tok_reqfifo) ? "..." : ""));
+	}
+#else
 	if (tcbp->tok_request)
 		printk(KERN_DEBUG " req: %s%s",
 			MSTR(objp,tcbp->tok_request->req_mode),
 			(tcbp->tok_request->req_next ? "..." : ""));
+#endif /* !TOKEN_FIFO_LINUX_LIST */
 }
 
 #if	defined(__KERNEL__)
@@ -232,35 +255,52 @@
 			/* Fifo manipluation routines */
 
 /* Initialize a fifo (make it empty) */
+#ifdef TOKEN_FIFO_LINUX_LIST
+static inline void
+#else
 STATIC void
-initfifo(struct fifo *fifop)
+#endif
+initfifo(fifo_t *fifop)
 {
+#ifdef TOKEN_FIFO_LINUX_LIST
+	INIT_LIST_HEAD(fifop);
+#else
 	fifop->fifo_req_begin = NULL;
 	/* For consistency just set end pointer to NULL also */
 	fifop->fifo_req_end = NULL;
+#endif
 }
 
 /* Place new entry onto end of a fifo */
+#ifdef TOKEN_FIFO_LINUX_LIST
+static inline void
+#else
 STATIC void
+#endif
 insertfifo(
-	struct fifo *fifop,
+	fifo_t *fifop,
 	struct nrreq *item)
 {
+#ifdef TOKEN_FIFO_LINUX_LIST
+	list_add_tail(&item->req_list, fifop);
+#else
 	if (fifop->fifo_req_begin == NULL) {	/* First entry */
 		fifop->fifo_req_begin = item;
 		fifop->fifo_req_end = item;
 	} else {
 		fifop->fifo_req_end->req_next = item;
-		smp_wmb();
+		mb();
 		fifop->fifo_req_end = item;
 	}
 	item->req_next = NULL;
 	return;
+#endif
 }
 
+#ifndef TOKEN_FIFO_LINUX_LIST
 /* Retrieve and remove an entry from the front of a fifo */
 STATIC struct nrreq *
-deletefifo(struct fifo *fifop)
+deletefifo(fifo_t *fifop)
 {
 	struct nrreq *save_req;
 
@@ -275,25 +315,47 @@
 		fifop->fifo_req_end = NULL;
 	return(save_req);
 }
+#endif /* !TOKEN_FIFO_LINUX_LIST */
 
 STATIC int
-countfifo(struct fifo *fifop)
+countfifo(fifo_t *fifop)
 {
+#ifdef TOKEN_FIFO_LINUX_LIST
+	struct list_head *pos;
+	int count = 0;
+
+	list_for_each(pos, fifop)
+		count++;
+#else
 	struct nrreq *req;
 	int count = 0;
 
 	for (req = fifop->fifo_req_begin ; req ; req = req->req_next)
 		count++;
+#endif
 	return count;
 }
 
 		/* Request structure manilpulation routines */
 
+#ifdef CLITOK_KMEM_CACHE
+STATIC kmem_cache_t *nrreq_cachep;
+
+void
+nrreq_init(void)
+{
+	nrreq_cachep = kmem_cache_create("clitok_req_cache",
+				sizeof(struct nrreq), 0,
+				SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_PANIC,
+				NULL, NULL);
+}
+#else
 /* Free request structures */
 STATIC struct nrreq *nrreq_free = NULL;
 STATIC int nrreq_free_count = 0;
 STATIC int nrreq_malloc_count = 0;
 SPIN_LOCK_T nrreq_free_lock;
+#endif
 
 /*
  * Pull a request structure off the free list or allocate a new one
@@ -303,6 +365,22 @@
 {
 	struct nrreq *reqp;
 
+#ifdef CLITOK_KMEM_CACHE
+	reqp = kmem_cache_alloc(nrreq_cachep, GFP_KERNEL|__GFP_NOFAIL);
+#ifdef TOKEN_FIFO_LINUX_LIST
+	INIT_LIST_HEAD(&reqp->req_list);
+#endif
+#ifdef __KERNEL__
+#ifdef CFSTOK_INTR
+	INIT_INTR_EVENT(&reqp->req_done);
+#else
+	INIT_EVENT(&reqp->req_done);
+#endif
+#ifdef CFSTOK_INTR
+	reqp->req_release = 0;
+#endif
+#endif
+#else /* !CLITOK_KMEM_CACHE */
 	LOCK_SPIN_LOCK(&nrreq_free_lock);
 	if ((reqp = nrreq_free)) {
 		nrreq_free = reqp->req_next;
@@ -321,6 +399,7 @@
 		INIT_EVENT(&reqp->req_done);
 #endif
 	}
+#endif /* !CLITOK_KMEM_CACHE */
 	return reqp;
 }
 
@@ -330,6 +409,14 @@
 void
 tok_freereq(struct nrreq *reqp)
 {
+#ifdef CLITOK_KMEM_CACHE
+#ifdef CFSTOK_INTR
+	DEINIT_INTR_EVENT(&reqp->req_done);
+#else
+	DEINIT_EVENT(&reqp->req_done);
+#endif
+	kmem_cache_free(nrreq_cachep, reqp);
+#else
 #ifdef DEBUG
 	reqp->req_agent = 0;
 #endif
@@ -339,12 +426,26 @@
 	nrreq_free = reqp;
 	nrreq_free_count++;
 	UNLOCK_SPIN_LOCK(&nrreq_free_lock);
+#endif /* !CLITOK_KMEM_CACHE */
 }
 
+#ifdef CLITOK_KMEM_CACHE
+STATIC kmem_cache_t *nrrevoke_cachep;
+
+void
+nrrevoke_init(void)
+{
+	nrrevoke_cachep = kmem_cache_create("clitok_rev_cache",
+				sizeof(struct nrrevoke), 0,
+				SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_PANIC,
+				NULL, NULL);
+}
+#else
 /* Free revoke structures */
 STATIC struct nrrevoke *nrrev_free = NULL;
 STATIC int nrrev_free_count = 0;
 SPIN_LOCK_T nrrev_free_lock;
+#endif
 
 /*
  * Pull a revoke structure off the free list or allocate a new one
@@ -354,6 +455,12 @@
 {
 	struct nrrevoke *revp;
 
+#ifdef CLITOK_KMEM_CACHE
+	revp = kmem_cache_alloc(nrrevoke_cachep, GFP_KERNEL|__GFP_NOFAIL);
+#ifdef TOKEN_FIFO_LINUX_LIST
+	INIT_LIST_HEAD(&revp->rev_reqfifo);
+#endif
+#else
 	LOCK_SPIN_LOCK(&nrrev_free_lock);
 	if ((revp = nrrev_free)) {
 		nrrev_free = (struct nrrevoke *)revp->rev_tcbp;
@@ -365,6 +472,7 @@
 		if (revp == NULL)
 			panic("allocnrrev: Can't malloc revoke struct\n");
 	}
+#endif
 	return revp;
 }
 
@@ -374,12 +482,16 @@
 STATIC void
 freerev(struct nrrevoke *revp)
 {
+#ifdef CLITOK_KMEM_CACHE
+	kmem_cache_free(nrrevoke_cachep, revp);
+#else
 	LOCK_SPIN_LOCK(&nrrev_free_lock);
 	revp->rev_tcbp = (nrtcb_t *)nrrev_free;
 	mb();
 	nrrev_free = revp;
 	nrrev_free_count++;
 	UNLOCK_SPIN_LOCK(&nrrev_free_lock);
+#endif
 }
 
 /*
@@ -416,15 +528,26 @@
 #if	defined(DEBUG) || defined(SIMUL)
 	char str[DBGSTRLEN];
 #endif
+#ifdef CFSTOK_INTR
+	extern int tok_release(object_t *, nrtcb_t *, int, int);
+#endif
 
 DBG(DBGCLI,("wakeup(0x%x) performed on requestor process\n", reqp));
 #ifdef __KERNEL__
+#ifdef CFSTOK_INTR
+	SIGNAL_INTR_EVENT(&reqp->req_done);
+	if (tas(&reqp->req_release)) {
+		(void) tok_release(objp, reqp->req_tcbp, reqp->req_mode, 0);
+		tok_freereq(reqp);
+	}
+#else
 	SIGNAL_EVENT(&(reqp->req_done));
 #ifdef NOTYET
 	if (reqp->req_interrupted) {
 		tok_release(reqp->tcbp, ...);
 	}
 #endif /* NOTYET */
+#endif /* !CFSTOK_INTR */
 #endif /* __KERNEL__ */
 #ifdef SIMUL
 	/* Change process Status */
@@ -466,12 +589,17 @@
 STATIC void
 process_reqfifo(
 	object_t *objp,
-	struct fifo *fifo)
+	fifo_t *fifo)
 {
 	struct nrreq *reqp;
+#ifdef TOKEN_FIFO_LINUX_LIST
+	struct nrreq *tmp;
 
+	list_for_each_entry_safe(reqp, tmp, fifo, req_list) {
+		list_del(&reqp->req_list);
+#else
 	while((reqp = (struct nrreq *)deletefifo(fifo))) {
-
+#endif
 		process_req(objp, reqp);
 	}
 }
@@ -622,12 +750,23 @@
  *
  * SMP: Unlike other token entry points no locks are held on entry
  */
+#ifdef CFSTOK_INTR
+int
+tok_wait(
+	void *cookie)
+#else
 void
 tok_wait(
 	nrtcb_t *tcbp,
 	void *cookie)
+#endif
 {
 	register struct nrreq *request = (struct nrreq *)cookie;
+#ifdef CFSTOK_INTR
+	int intr;
+
+	extern int tok_release(object_t *, nrtcb_t *, int, int);
+#endif
 
 #ifdef __KERNEL__
 	SSI_ASSERT_NO_SPIN_LOCKS_HELD();
@@ -635,9 +774,22 @@
 
 	DBG(DBGCLI,("tok_hold: wait(0x%x) request in process...\n", request));
 #ifdef __KERNEL__
+#ifdef CFSTOK_INTR
+	WAIT_INTR_EVENT(&request->req_done, 1, &intr);
+	if (!intr) {
+		tok_freereq(request);
+	} else if (tas(&request->req_release)) {
+		/* Lost the race */
+		(void) tok_release(request->req_objp, request->req_tcbp,
+				request->req_mode, 0);
+		tok_freereq(request);
+	}
+	DBG(DBGCLI,("tok_hold: request(0x%x) complete returning\n", request));
+	return intr;
+#else /* !CFSTOK_INTR */
 	WAIT_EVENT(&(request->req_done));
 #ifdef NOTYET
-	if (!WAIT_EVENT_SIG(&(request->req_done, PRIHI)) {
+	if (!WAIT_EVENT_SIG(&(request->req_done, PRIHI))) {
 		/* NSC_XXX: Need a lock here */
 		request->req_XXXX = XXXinterrupted;
 		return FALSE;
@@ -645,8 +797,8 @@
 #endif
 	tok_freereq(request);
 	DBG(DBGCLI,("tok_hold: request(0x%x) complete returning\n", request));
+#endif /* !CFSTOK_INTR */
 #endif /* __KERNEL__ */
-	return;
 }
 
 int
@@ -732,7 +884,11 @@
 	 * request will get the the hold, release it, and then the
 	 * revoke will take affect.
 	 */
+#ifdef TOKEN_FIFO_LINUX_LIST
+	SSI_ASSERT(!(flags & TOK_RECURSIVE) || tcbp->tok_hld_count || tcbp->tok_revptr == NULL || (_tok_request(tcbp) && tcbp->tok_revptr && tok_request(tcbp)->req_mode == tcbp->tok_revptr->rev_oldmode));
+#else
 	SSI_ASSERT(!(flags & TOK_RECURSIVE) || tcbp->tok_hld_count || tcbp->tok_revptr == NULL || (tcbp->tok_request && tcbp->tok_revptr && tcbp->tok_request->req_mode == tcbp->tok_revptr->rev_oldmode));
+#endif
 
 	if (!ALLOWS(objp, (int)tcbp->tok_mode, request->req_mode)) {
 
@@ -743,7 +899,11 @@
 		SSI_ASSERT(!(flags & TOK_RECURSIVE));
 
 		/* If someone already requesting, just wait */
+#ifdef TOKEN_FIFO_LINUX_LIST
+		if (_tok_request(tcbp)) {
+#else
 		if (tcbp->tok_request) {
+#endif
 
 			DBG(DBGCLI|DBGSIM,("CLI M waiting %s %d reqmode %s\n",
 				DSPOBJ(objp,str),
@@ -857,6 +1017,60 @@
 	return TRUE;
 }
 
+static inline void
+__token_wait_and_hold(
+	object_t *objp,
+	nrtcb_t *tcbp,
+	int newmode)
+{
+#ifdef TOKEN_FIFO_LINUX_LIST
+	struct nrreq *request, *tmp;
+#else
+	struct nrreq *request;
+#endif
+	fifo_t fifo;
+
+	/* Wakeup and hold token for all requestors that are compatible */
+#ifdef TOKEN_FIFO_LINUX_LIST
+	list_for_each_entry_safe(request, tmp, &tcbp->tok_reqfifo, req_list) {
+#else
+	fifo = tcbp->tok_reqfifo;
+	initfifo(&(tcbp->tok_reqfifo));		/* Clear req fifo */
+	while((request = deletefifo(&fifo))) {
+#endif
+		if (ALLOWS(objp, newmode, request->req_mode)) {
+#ifdef TOKEN_FIFO_LINUX_LIST
+			list_del(&request->req_list);
+#endif
+
+			SSI_ASSERT((tcbp)->tok_hld_count < HLD_COUNT_MAX);
+
+			if (!((tcbp)->tok_hld_count < HLD_COUNT_MAX))
+				printk(KERN_WARNING "tok_reqfail: too many holds\n");
+			else
+				tcbp->tok_hld_count++;
+
+			/* Wakeup this requestor */
+			complete_req(objp, request);
+#ifndef TOKEN_FIFO_LINUX_LIST
+		} else {
+			/* This request isn't compatible, handle it below */
+			insertfifo(&tcbp->tok_reqfifo, request);
+#endif
+		}
+	}
+
+	/* Process other requests which had to wait */
+#ifdef TOKEN_FIFO_LINUX_LIST
+	INIT_LIST_HEAD(&fifo);
+	list_splice_init(&tcbp->tok_reqfifo, &fifo);
+#else
+	fifo = tcbp->tok_reqfifo;
+	initfifo(&(tcbp->tok_reqfifo));		/* Clear req fifo */
+#endif
+	process_reqfifo(objp, &fifo);
+}
+
 /*
  * Process grant of a request for token
  * 	We only accept grants that client requested.  Unsolicited
@@ -873,8 +1087,6 @@
 	int oldmode,
 	int newmode)
 {
-	struct nrreq *request;
-	struct fifo fifo;
 #if	defined(DEBUG) || defined(SIMUL)
 	char str[DBGSTRLEN];
 #endif
@@ -889,38 +1101,19 @@
 
 	SSI_ASSERT(tcbp->tok_mode == oldmode);
 
+#ifdef TOKEN_FIFO_LINUX_LIST
+	SSI_ASSERT(_tok_request(tcbp));
+	/* The first requestor on fifo sent request, he must be compatible */
+	SSI_ASSERT(ALLOWS(objp, newmode, (int)tok_request(tcbp)->req_mode));
+#else
 	SSI_ASSERT(tcbp->tok_request);
 	/* The first requestor on fifo sent request, he must be compatible */
 	SSI_ASSERT(ALLOWS(objp, newmode, (int)tcbp->tok_request->req_mode));
+#endif
 
 	tcbp->tok_mode = newmode;
 
-	/* Wakeup and hold token for all requestors that are compatible */
-	fifo = tcbp->tok_reqfifo;
-	initfifo(&(tcbp->tok_reqfifo));		/* Clear req fifo */
-	while((request = deletefifo(&fifo))) {
-
-		if (ALLOWS(objp, newmode, request->req_mode)) {
-
-			SSI_ASSERT((tcbp)->tok_hld_count < HLD_COUNT_MAX);
-
-			if (!((tcbp)->tok_hld_count < HLD_COUNT_MAX))
-				printk(KERN_WARNING "tok_grant: too many holds\n");
-			else
-			tcbp->tok_hld_count++;
-
-			/* Wakeup this requestor */
-			complete_req(objp, request);
-		} else {
-			/* This request isn't compatible, handle it below */
-			insertfifo(&tcbp->tok_reqfifo, request);
-		}
-	}
-
-	/* Process other requests which had to wait */
-	fifo = tcbp->tok_reqfifo;
-	initfifo(&(tcbp->tok_reqfifo));		/* Clear req fifo */
-	process_reqfifo(objp, &fifo);
+	__token_wait_and_hold(objp, tcbp, newmode);
 }
 
 /*
@@ -1016,8 +1209,13 @@
 	 * that is pending.  If a request is pending the revoke might have
 	 * arrived before the grant.
 	 */
+#ifdef TOKEN_FIFO_LINUX_LIST
+	if (oldmode != tcbp->tok_mode && (!_tok_request(tcbp) ||
+		(oldmode != tok_request(tcbp)->req_mode)))
+#else
 	if (oldmode != tcbp->tok_mode && ((tcbp->tok_request == NULL) ||
 		(oldmode != tcbp->tok_request->req_mode)))
+#endif
 		return FALSE;
 
 	return TRUE;
@@ -1043,9 +1241,15 @@
 		MSTR(objp,revp->rev_oldmode), MSTR(objp,revp->rev_newmode)));
 
 	/* check_revok() should catch prevent us SSI_ASSERTing here */
+#ifdef TOKEN_FIFO_LINUX_LIST
+	SSI_ASSERT(revp->rev_oldmode == tcbp->tok_mode ||
+		(_tok_request(tcbp) &&
+		(revp->rev_oldmode == tok_request(tcbp)->req_mode)));
+#else
 	SSI_ASSERT(revp->rev_oldmode == tcbp->tok_mode ||
 		((tcbp->tok_request != NULL) &&
 		(revp->rev_oldmode == tcbp->tok_request->req_mode)));
+#endif
 
 	if (tcbp->tok_revptr != revp) {
 		/* If run into revoke we've recieved overlapping
@@ -1064,8 +1268,13 @@
 		 * the revoke, if it's needs to be processed after the grant
 		 * and subsequent release
 		 */
+#ifdef TOKEN_FIFO_LINUX_LIST
+		if (_tok_request(tcbp) &&
+			tok_request(tcbp)->req_mode == revp->rev_oldmode)
+#else
 		if (tcbp->tok_request &&
 			tcbp->tok_request->req_mode == revp->rev_oldmode)
+#endif
 			return FALSE;
 	}
 
@@ -1126,8 +1335,13 @@
 	/* No need to lock TCB because we must be protected by caller */
 
 	/* NSC_XXX: Should be return status here instead of panic? */
+#ifdef TOKEN_FIFO_LINUX_LIST
+	if (tcbp->tok_mode != TOK_NOMODE || _tok_request(tcbp) ||
+	    tcbp->tok_revptr)
+#else
 	if (tcbp->tok_mode != TOK_NOMODE || tcbp->tok_request ||
 	    tcbp->tok_revptr)
+#endif
 		panic("tok_destroy: Tokens still in use");
 
 	return;
@@ -1190,20 +1404,26 @@
 	int newmode,
 	agent_t agent)
 {
-	struct fifo fifo;
-	struct nrreq *request;
 	struct nrrevoke *revp;
 #if	defined(DEBUG) || defined(SIMUL)
 	char str[DBGSTRLEN];
 #endif
 
 	SSI_ASSERT(tcbp);
+#ifdef TOKEN_FIFO_LINUX_LIST
+	SSI_ASSERT(_tok_request(tcbp));
+#else
 	SSI_ASSERT(tcbp->tok_request);
+#endif
 	/* The old mode can be TOK_NOMODE */
 	SSI_ASSERT(oldmode >= 0 && oldmode < TOK_NUMMODES(objp));
 	SSI_ASSERT(newmode > 0 && newmode < TOK_NUMMODES(objp));
 	/* Check that state is correct as set by requesting code */
+#ifdef TOKEN_FIFO_LINUX_LIST
+	SSI_ASSERT(tok_request(tcbp)->req_mode ==  newmode);
+#else
 	SSI_ASSERT(tcbp->tok_request->req_mode ==  newmode);
+#endif
 
 	DBG(DBGCLI|DBGSIM,("CLI R reqfail %s %d from:%s to:%s\n",
 		DSPOBJ(objp,str), agent,
@@ -1242,40 +1462,24 @@
 	if (!ALLOWS(objp, (int)tcbp->tok_mode, newmode)) {
 
 DBG(DBGCLI|DBGSIM|DBGREQUEST,("CLI S request %s %d from:%s to:%s (reqfail)\n",
+#ifdef TOKEN_FIFO_LINUX_LIST
+DSPOBJ(objp,str), tok_request(tcbp)->req_agent,
+#else
 DSPOBJ(objp,str), tcbp->tok_request->req_agent,
+#endif
 MSTR(objp,tcbp->tok_mode), MSTR(objp,newmode)));
 
+#ifdef TOKEN_FIFO_LINUX_LIST
+		MSGSEND(objp, REQUEST, (range_off_t)0, (range_off_t)0, tok_request(tcbp)->req_agent,
+			tcbp->tok_mode, tok_request(tcbp)->req_mode);
+#else
 		MSGSEND(objp, REQUEST, (range_off_t)0, (range_off_t)0, tcbp->tok_request->req_agent,
 			tcbp->tok_mode, tcbp->tok_request->req_mode);
+#endif
 		return;
 	}
 
-	/* Wakeup and hold token for all requestors that are compatible */
-	fifo = tcbp->tok_reqfifo;
-	initfifo(&(tcbp->tok_reqfifo));		/* Clear req fifo */
-	while((request = deletefifo(&fifo))) {
-
-		if (ALLOWS(objp, newmode, request->req_mode)) {
-
-			SSI_ASSERT((tcbp)->tok_hld_count < HLD_COUNT_MAX);
-
-			if (!((tcbp)->tok_hld_count < HLD_COUNT_MAX))
-				printk(KERN_WARNING "tok_reqfail: too many holds\n");
-			else
-			tcbp->tok_hld_count++;
-
-			/* Wakeup this requestor */
-			complete_req(objp, request);
-		} else {
-			/* This request isn't compatible, handle it below */
-			insertfifo(&tcbp->tok_reqfifo, request);
-		}
-	}
-
-	/* Process other requests which had to wait */
-	fifo = tcbp->tok_reqfifo;
-	initfifo(&(tcbp->tok_reqfifo));		/* Clear req fifo */
-	process_reqfifo(objp, &fifo);
+	__token_wait_and_hold(objp, tcbp, newmode);
 }
 
 int
@@ -1293,7 +1497,11 @@
 	}
 
 	/* If holds, requests or revokes, don't do anything */
+#ifdef TOKEN_FIFO_LINUX_LIST
+	if (tcbp->tok_hld_count || _tok_request(tcbp) || tcbp->tok_revptr) {
+#else
 	if (tcbp->tok_hld_count || tcbp->tok_request || tcbp->tok_revptr) {
+#endif
 		return(FALSE);
 	}
 
@@ -1379,12 +1587,14 @@
 			retp->eff_mode = TOK_NOMODE;
 	} else
 		retp->flags |= CLISTAT_NOTREVOKED;
+#ifdef TOKEN_FIFO_LINUX_LIST
+	if (_tok_request(tcbp)) {
+#else
 	if (tcbp->tok_request) {
+#endif
 		retp->flags |= CLISTAT_REQPEND;
 	}
-	retp->mode = tcbp->tok_mode;
-	mb();
-	retp->uc_mode = retp->mode;
+	retp->uc_mode = retp->mode = tcbp->tok_mode;
 	retp->hold_count = tcbp->tok_hld_count;
 
 #ifdef DEBUG
@@ -1405,7 +1615,7 @@
 	nrtcb_t *tcbp)
 {
 	struct nrrevoke *revp;
-	struct fifo fifo;
+	fifo_t fifo;
 #if	defined(DEBUG) || defined(SIMUL)
 	char str[DBGSTRLEN];
 #endif
@@ -1415,16 +1625,26 @@
 	tcbp->tok_mode = TOK_ALL(objp);
 
 	if ((revp = tcbp->tok_revptr)) {
+#ifdef TOKEN_FIFO_LINUX_LIST
+		INIT_LIST_HEAD(&fifo);
+		list_splice_init(&revp->rev_reqfifo, &fifo);
+#else
 		fifo = revp->rev_reqfifo;
 		initfifo(&(revp->rev_reqfifo));
+#endif
 		freerev(revp);
 		tcbp->tok_revptr = NULL;
 		process_reqfifo(objp, &fifo);
 	}
 
 	/* Process requests waiting for grant */
+#ifdef TOKEN_FIFO_LINUX_LIST
+	INIT_LIST_HEAD(&fifo);
+	list_splice_init(&tcbp->tok_reqfifo, &fifo);
+#else
 	fifo = tcbp->tok_reqfifo;
 	initfifo(&(tcbp->tok_reqfifo));
+#endif
 	process_reqfifo(objp, &fifo);
 }
 #endif
@@ -1496,8 +1716,10 @@
 	agent_t agent,
 	int do_haves)
 {
-	struct fifo fifo;
+	fifo_t fifo;
+#ifndef TOKEN_FIFO_LINUX_LIST
 	struct nrreq *request;
+#endif
 	struct nrrevoke *revp;
 #if	defined(DEBUG) || defined(SIMUL)
 	char str[DBGSTRLEN];
@@ -1521,23 +1743,36 @@
 		 * have to re-send to us, anyway.
 		 */
 		if ((revp = tcbp->tok_revptr)) {
+#ifdef TOKEN_FIFO_LINUX_LIST
+			/* Merge revoke reqfifo with tok_reqfifo */
+			list_splice_init(&revp->rev_reqfifo, &tcbp->tok_reqfifo);
+#else
 			fifo = revp->rev_reqfifo;
 			initfifo(&(revp->rev_reqfifo));
+#endif
 			freerev(revp);
 			tcbp->tok_revptr = NULL;
 
+#ifndef TOKEN_FIFO_LINUX_LIST
 			/* Merge revoke reqfifo with tok_reqfifo */
 			while((request = deletefifo(&fifo)))
 				insertfifo(&tcbp->tok_reqfifo, request);
+#endif
 		}
 
 		/* Process all pending requests */
+#ifdef TOKEN_FIFO_LINUX_LIST
+		INIT_LIST_HEAD(&fifo);
+		list_splice_init(&tcbp->tok_reqfifo, &fifo);
+#else
 		fifo = tcbp->tok_reqfifo;
 		initfifo(&(tcbp->tok_reqfifo));		/* Clear req fifo */
+#endif
 		process_reqfifo(objp, &fifo);
 	}
 }
 
+#ifndef CLITOK_KMEM_CACHE
 int
 clitok_req_free_memory(int count, unsigned int gfp_mask)
 {
@@ -1610,3 +1845,4 @@
 
 	return nrrev_free_count;
 }
+#endif /* !CLITOK_KMEM_CACHE */

Index: mhsvrtok.c
===================================================================
RCS file: /cvsroot/ssic-linux/openssi/kernel/cluster/ssi/token/mhsvrtok.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- mhsvrtok.c	10 Oct 2008 08:10:32 -0000	1.6
+++ mhsvrtok.c	3 Feb 2009 06:18:12 -0000	1.7
@@ -70,9 +70,22 @@
 
 			/* Multi-node support routines */
 
+#ifdef MHSVRTOK_KMEM_CACHE
+STATIC kmem_cache_t *mhnode_cachep;
+
+void
+mhnode_init(void)
+{
+	mhnode_cachep = kmem_cache_create("mhsvrtok_cache",
+				sizeof(struct mhnode_ent), 0,
+				SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_PANIC,
+				NULL, NULL);
+}
+#else
 STATIC struct mhnode_ent *mhnode_free = NULL;
 STATIC int mhnode_free_count = 0;
 SPIN_LOCK_T mhnode_free_lock;
+#endif
 
 STATIC struct mhnode_ent *
 mhsvrallocnode(agent_t agent)
@@ -81,6 +94,9 @@
 
 	SSI_ASSERT(agent != 0);
 
+#ifdef MHSVRTOK_KMEM_CACHE
+	newnode = kmem_cache_alloc(mhnode_cachep, GFP_KERNEL|__GFP_NOFAIL);
+#else
 	LOCK_SPIN_LOCK(&mhnode_free_lock);
 	if ((newnode = mhnode_free)) {
 		mhnode_free = newnode->nd_next;
@@ -92,6 +108,7 @@
 		if (newnode == NULL)
 			panic("svrallocnode: Can't malloc node struct\n");
 	}
+#endif
 	newnode->nd_agent = agent;
 	newnode->nd_numtoks = 0;
 	newnode->nd_next = NULL;
@@ -119,6 +136,9 @@
 	/* If the list is empty the ndcount must be zero */
 	SSI_ASSERT(tcbp->tok_ndcount == 0 || tcbp->tok_ndlist);
 
+#ifdef MHSVRTOK_KMEM_CACHE
+	kmem_cache_free(mhnode_cachep, savep);
+#else
 	/* Put node entry only free list */
 	LOCK_SPIN_LOCK(&mhnode_free_lock);
 	savep->nd_next = mhnode_free;
@@ -128,6 +148,7 @@
 	mhnode_free = savep;
 	mhnode_free_count++;
 	UNLOCK_SPIN_LOCK(&mhnode_free_lock);
+#endif /* !MHSVRTOK_KMEM_CACHE */
 }
 
 /*
@@ -453,6 +474,7 @@
 }
 #endif	/* !__KERNEL__ || DEBUG || DEBUG_TOOLS */
 
+#ifndef MHSVRTOK_KMEM_CACHE
 int
 mhsvrtok_free_memory(int count, unsigned int gfp_mask)
 {
@@ -488,3 +510,4 @@
 	}
 	return mhnode_free_count;
 }
+#endif /* !MHSVRTOK_KMEM_CACHE */


------------------------------------------------------------------------------
Create and Deploy Rich Internet Apps outside the browser with Adobe(R)AIR(TM)
software. With Adobe AIR, Ajax developers can use existing skills and code to
build responsive, highly engaging applications that combine the power of local
resources and data with the reach of the web. Download the Adobe AIR SDK and
Ajax docs to start building applications today-http://p.sf.net/sfu/adobe-com
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.