forked from mirrors/linux
		
	net, rds, Replace xlist in net/rds/xlist.h with llist
The functionality of xlist and llist is almost same. This patch replace xlist with llist to avoid code duplication. Known issues: don't know how to test this, need special hardware? Signed-off-by: Huang Ying <ying.huang@intel.com> Cc: Chris Mason <chris.mason@oracle.com> Cc: Andy Grover <andy.grover@oracle.com> Cc: "David S. Miller" <davem@davemloft.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									dc00fd4441
								
							
						
					
					
						commit
						1bc144b625
					
				
					 3 changed files with 51 additions and 140 deletions
				
			
		| 
						 | 
					@ -9,6 +9,7 @@ config RDS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config RDS_RDMA
 | 
					config RDS_RDMA
 | 
				
			||||||
	tristate "RDS over Infiniband and iWARP"
 | 
						tristate "RDS over Infiniband and iWARP"
 | 
				
			||||||
 | 
						select LLIST
 | 
				
			||||||
	depends on RDS && INFINIBAND && INFINIBAND_ADDR_TRANS
 | 
						depends on RDS && INFINIBAND && INFINIBAND_ADDR_TRANS
 | 
				
			||||||
	---help---
 | 
						---help---
 | 
				
			||||||
	  Allow RDS to use Infiniband and iWARP as a transport.
 | 
						  Allow RDS to use Infiniband and iWARP as a transport.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,10 +33,10 @@
 | 
				
			||||||
#include <linux/kernel.h>
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <linux/rculist.h>
 | 
					#include <linux/rculist.h>
 | 
				
			||||||
 | 
					#include <linux/llist.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "rds.h"
 | 
					#include "rds.h"
 | 
				
			||||||
#include "ib.h"
 | 
					#include "ib.h"
 | 
				
			||||||
#include "xlist.h"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static DEFINE_PER_CPU(unsigned long, clean_list_grace);
 | 
					static DEFINE_PER_CPU(unsigned long, clean_list_grace);
 | 
				
			||||||
#define CLEAN_LIST_BUSY_BIT 0
 | 
					#define CLEAN_LIST_BUSY_BIT 0
 | 
				
			||||||
| 
						 | 
					@ -49,7 +49,7 @@ struct rds_ib_mr {
 | 
				
			||||||
	struct rds_ib_mr_pool	*pool;
 | 
						struct rds_ib_mr_pool	*pool;
 | 
				
			||||||
	struct ib_fmr		*fmr;
 | 
						struct ib_fmr		*fmr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct xlist_head	xlist;
 | 
						struct llist_node	llnode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* unmap_list is for freeing */
 | 
						/* unmap_list is for freeing */
 | 
				
			||||||
	struct list_head	unmap_list;
 | 
						struct list_head	unmap_list;
 | 
				
			||||||
| 
						 | 
					@ -71,9 +71,9 @@ struct rds_ib_mr_pool {
 | 
				
			||||||
	atomic_t		item_count;		/* total # of MRs */
 | 
						atomic_t		item_count;		/* total # of MRs */
 | 
				
			||||||
	atomic_t		dirty_count;		/* # dirty of MRs */
 | 
						atomic_t		dirty_count;		/* # dirty of MRs */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct xlist_head	drop_list;		/* MRs that have reached their max_maps limit */
 | 
						struct llist_head	drop_list;		/* MRs that have reached their max_maps limit */
 | 
				
			||||||
	struct xlist_head	free_list;		/* unused MRs */
 | 
						struct llist_head	free_list;		/* unused MRs */
 | 
				
			||||||
	struct xlist_head	clean_list;		/* global unused & unamapped MRs */
 | 
						struct llist_head	clean_list;		/* global unused & unamapped MRs */
 | 
				
			||||||
	wait_queue_head_t	flush_wait;
 | 
						wait_queue_head_t	flush_wait;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	atomic_t		free_pinned;		/* memory pinned by free MRs */
 | 
						atomic_t		free_pinned;		/* memory pinned by free MRs */
 | 
				
			||||||
| 
						 | 
					@ -220,9 +220,9 @@ struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_ibdev)
 | 
				
			||||||
	if (!pool)
 | 
						if (!pool)
 | 
				
			||||||
		return ERR_PTR(-ENOMEM);
 | 
							return ERR_PTR(-ENOMEM);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	INIT_XLIST_HEAD(&pool->free_list);
 | 
						init_llist_head(&pool->free_list);
 | 
				
			||||||
	INIT_XLIST_HEAD(&pool->drop_list);
 | 
						init_llist_head(&pool->drop_list);
 | 
				
			||||||
	INIT_XLIST_HEAD(&pool->clean_list);
 | 
						init_llist_head(&pool->clean_list);
 | 
				
			||||||
	mutex_init(&pool->flush_lock);
 | 
						mutex_init(&pool->flush_lock);
 | 
				
			||||||
	init_waitqueue_head(&pool->flush_wait);
 | 
						init_waitqueue_head(&pool->flush_wait);
 | 
				
			||||||
	INIT_DELAYED_WORK(&pool->flush_worker, rds_ib_mr_pool_flush_worker);
 | 
						INIT_DELAYED_WORK(&pool->flush_worker, rds_ib_mr_pool_flush_worker);
 | 
				
			||||||
| 
						 | 
					@ -260,26 +260,18 @@ void rds_ib_destroy_mr_pool(struct rds_ib_mr_pool *pool)
 | 
				
			||||||
	kfree(pool);
 | 
						kfree(pool);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void refill_local(struct rds_ib_mr_pool *pool, struct xlist_head *xl,
 | 
					 | 
				
			||||||
			 struct rds_ib_mr **ibmr_ret)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct xlist_head *ibmr_xl;
 | 
					 | 
				
			||||||
	ibmr_xl = xlist_del_head_fast(xl);
 | 
					 | 
				
			||||||
	*ibmr_ret = list_entry(ibmr_xl, struct rds_ib_mr, xlist);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline struct rds_ib_mr *rds_ib_reuse_fmr(struct rds_ib_mr_pool *pool)
 | 
					static inline struct rds_ib_mr *rds_ib_reuse_fmr(struct rds_ib_mr_pool *pool)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rds_ib_mr *ibmr = NULL;
 | 
						struct rds_ib_mr *ibmr = NULL;
 | 
				
			||||||
	struct xlist_head *ret;
 | 
						struct llist_node *ret;
 | 
				
			||||||
	unsigned long *flag;
 | 
						unsigned long *flag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	preempt_disable();
 | 
						preempt_disable();
 | 
				
			||||||
	flag = &__get_cpu_var(clean_list_grace);
 | 
						flag = &__get_cpu_var(clean_list_grace);
 | 
				
			||||||
	set_bit(CLEAN_LIST_BUSY_BIT, flag);
 | 
						set_bit(CLEAN_LIST_BUSY_BIT, flag);
 | 
				
			||||||
	ret = xlist_del_head(&pool->clean_list);
 | 
						ret = llist_del_first(&pool->clean_list);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		ibmr = list_entry(ret, struct rds_ib_mr, xlist);
 | 
							ibmr = llist_entry(ret, struct rds_ib_mr, llnode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	clear_bit(CLEAN_LIST_BUSY_BIT, flag);
 | 
						clear_bit(CLEAN_LIST_BUSY_BIT, flag);
 | 
				
			||||||
	preempt_enable();
 | 
						preempt_enable();
 | 
				
			||||||
| 
						 | 
					@ -529,46 +521,44 @@ static inline unsigned int rds_ib_flush_goal(struct rds_ib_mr_pool *pool, int fr
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * given an xlist of mrs, put them all into the list_head for more processing
 | 
					 * given an llist of mrs, put them all into the list_head for more processing
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void xlist_append_to_list(struct xlist_head *xlist, struct list_head *list)
 | 
					static void llist_append_to_list(struct llist_head *llist, struct list_head *list)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rds_ib_mr *ibmr;
 | 
						struct rds_ib_mr *ibmr;
 | 
				
			||||||
	struct xlist_head splice;
 | 
						struct llist_node *node;
 | 
				
			||||||
	struct xlist_head *cur;
 | 
						struct llist_node *next;
 | 
				
			||||||
	struct xlist_head *next;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	splice.next = NULL;
 | 
						node = llist_del_all(llist);
 | 
				
			||||||
	xlist_splice(xlist, &splice);
 | 
						while (node) {
 | 
				
			||||||
	cur = splice.next;
 | 
							next = node->next;
 | 
				
			||||||
	while (cur) {
 | 
							ibmr = llist_entry(node, struct rds_ib_mr, llnode);
 | 
				
			||||||
		next = cur->next;
 | 
					 | 
				
			||||||
		ibmr = list_entry(cur, struct rds_ib_mr, xlist);
 | 
					 | 
				
			||||||
		list_add_tail(&ibmr->unmap_list, list);
 | 
							list_add_tail(&ibmr->unmap_list, list);
 | 
				
			||||||
		cur = next;
 | 
							node = next;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * this takes a list head of mrs and turns it into an xlist of clusters.
 | 
					 * this takes a list head of mrs and turns it into linked llist nodes
 | 
				
			||||||
 * each cluster has an xlist of MR_CLUSTER_SIZE mrs that are ready for
 | 
					 * of clusters.  Each cluster has linked llist nodes of
 | 
				
			||||||
 * reuse.
 | 
					 * MR_CLUSTER_SIZE mrs that are ready for reuse.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void list_append_to_xlist(struct rds_ib_mr_pool *pool,
 | 
					static void list_to_llist_nodes(struct rds_ib_mr_pool *pool,
 | 
				
			||||||
				struct list_head *list, struct xlist_head *xlist,
 | 
									struct list_head *list,
 | 
				
			||||||
				struct xlist_head **tail_ret)
 | 
									struct llist_node **nodes_head,
 | 
				
			||||||
 | 
									struct llist_node **nodes_tail)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rds_ib_mr *ibmr;
 | 
						struct rds_ib_mr *ibmr;
 | 
				
			||||||
	struct xlist_head *cur_mr = xlist;
 | 
						struct llist_node *cur = NULL;
 | 
				
			||||||
	struct xlist_head *tail_mr = NULL;
 | 
						struct llist_node **next = nodes_head;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(ibmr, list, unmap_list) {
 | 
						list_for_each_entry(ibmr, list, unmap_list) {
 | 
				
			||||||
		tail_mr = &ibmr->xlist;
 | 
							cur = &ibmr->llnode;
 | 
				
			||||||
		tail_mr->next = NULL;
 | 
							*next = cur;
 | 
				
			||||||
		cur_mr->next = tail_mr;
 | 
							next = &cur->next;
 | 
				
			||||||
		cur_mr = tail_mr;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	*tail_ret = tail_mr;
 | 
						*next = NULL;
 | 
				
			||||||
 | 
						*nodes_tail = cur;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -581,8 +571,8 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool,
 | 
				
			||||||
			        int free_all, struct rds_ib_mr **ibmr_ret)
 | 
								        int free_all, struct rds_ib_mr **ibmr_ret)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rds_ib_mr *ibmr, *next;
 | 
						struct rds_ib_mr *ibmr, *next;
 | 
				
			||||||
	struct xlist_head clean_xlist;
 | 
						struct llist_node *clean_nodes;
 | 
				
			||||||
	struct xlist_head *clean_tail;
 | 
						struct llist_node *clean_tail;
 | 
				
			||||||
	LIST_HEAD(unmap_list);
 | 
						LIST_HEAD(unmap_list);
 | 
				
			||||||
	LIST_HEAD(fmr_list);
 | 
						LIST_HEAD(fmr_list);
 | 
				
			||||||
	unsigned long unpinned = 0;
 | 
						unsigned long unpinned = 0;
 | 
				
			||||||
| 
						 | 
					@ -603,7 +593,7 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			prepare_to_wait(&pool->flush_wait, &wait,
 | 
								prepare_to_wait(&pool->flush_wait, &wait,
 | 
				
			||||||
					TASK_UNINTERRUPTIBLE);
 | 
										TASK_UNINTERRUPTIBLE);
 | 
				
			||||||
			if (xlist_empty(&pool->clean_list))
 | 
								if (llist_empty(&pool->clean_list))
 | 
				
			||||||
				schedule();
 | 
									schedule();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ibmr = rds_ib_reuse_fmr(pool);
 | 
								ibmr = rds_ib_reuse_fmr(pool);
 | 
				
			||||||
| 
						 | 
					@ -628,10 +618,10 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool,
 | 
				
			||||||
	/* Get the list of all MRs to be dropped. Ordering matters -
 | 
						/* Get the list of all MRs to be dropped. Ordering matters -
 | 
				
			||||||
	 * we want to put drop_list ahead of free_list.
 | 
						 * we want to put drop_list ahead of free_list.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	xlist_append_to_list(&pool->drop_list, &unmap_list);
 | 
						llist_append_to_list(&pool->drop_list, &unmap_list);
 | 
				
			||||||
	xlist_append_to_list(&pool->free_list, &unmap_list);
 | 
						llist_append_to_list(&pool->free_list, &unmap_list);
 | 
				
			||||||
	if (free_all)
 | 
						if (free_all)
 | 
				
			||||||
		xlist_append_to_list(&pool->clean_list, &unmap_list);
 | 
							llist_append_to_list(&pool->clean_list, &unmap_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	free_goal = rds_ib_flush_goal(pool, free_all);
 | 
						free_goal = rds_ib_flush_goal(pool, free_all);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -663,22 +653,22 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool,
 | 
				
			||||||
	if (!list_empty(&unmap_list)) {
 | 
						if (!list_empty(&unmap_list)) {
 | 
				
			||||||
		/* we have to make sure that none of the things we're about
 | 
							/* we have to make sure that none of the things we're about
 | 
				
			||||||
		 * to put on the clean list would race with other cpus trying
 | 
							 * to put on the clean list would race with other cpus trying
 | 
				
			||||||
		 * to pull items off.  The xlist would explode if we managed to
 | 
							 * to pull items off.  The llist would explode if we managed to
 | 
				
			||||||
		 * remove something from the clean list and then add it back again
 | 
							 * remove something from the clean list and then add it back again
 | 
				
			||||||
		 * while another CPU was spinning on that same item in xlist_del_head.
 | 
							 * while another CPU was spinning on that same item in llist_del_first.
 | 
				
			||||||
		 *
 | 
							 *
 | 
				
			||||||
		 * This is pretty unlikely, but just in case  wait for an xlist grace period
 | 
							 * This is pretty unlikely, but just in case  wait for an llist grace period
 | 
				
			||||||
		 * here before adding anything back into the clean list.
 | 
							 * here before adding anything back into the clean list.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		wait_clean_list_grace();
 | 
							wait_clean_list_grace();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		list_append_to_xlist(pool, &unmap_list, &clean_xlist, &clean_tail);
 | 
							list_to_llist_nodes(pool, &unmap_list, &clean_nodes, &clean_tail);
 | 
				
			||||||
		if (ibmr_ret)
 | 
							if (ibmr_ret)
 | 
				
			||||||
			refill_local(pool, &clean_xlist, ibmr_ret);
 | 
								*ibmr_ret = llist_entry(clean_nodes, struct rds_ib_mr, llnode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* refill_local may have emptied our list */
 | 
							/* more than one entry in llist nodes */
 | 
				
			||||||
		if (!xlist_empty(&clean_xlist))
 | 
							if (clean_nodes->next)
 | 
				
			||||||
			xlist_add(clean_xlist.next, clean_tail, &pool->clean_list);
 | 
								llist_add_batch(clean_nodes->next, clean_tail, &pool->clean_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -711,9 +701,9 @@ void rds_ib_free_mr(void *trans_private, int invalidate)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Return it to the pool's free list */
 | 
						/* Return it to the pool's free list */
 | 
				
			||||||
	if (ibmr->remap_count >= pool->fmr_attr.max_maps)
 | 
						if (ibmr->remap_count >= pool->fmr_attr.max_maps)
 | 
				
			||||||
		xlist_add(&ibmr->xlist, &ibmr->xlist, &pool->drop_list);
 | 
							llist_add(&ibmr->llnode, &pool->drop_list);
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		xlist_add(&ibmr->xlist, &ibmr->xlist, &pool->free_list);
 | 
							llist_add(&ibmr->llnode, &pool->free_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	atomic_add(ibmr->sg_len, &pool->free_pinned);
 | 
						atomic_add(ibmr->sg_len, &pool->free_pinned);
 | 
				
			||||||
	atomic_inc(&pool->dirty_count);
 | 
						atomic_inc(&pool->dirty_count);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,80 +0,0 @@
 | 
				
			||||||
#ifndef _LINUX_XLIST_H
 | 
					 | 
				
			||||||
#define _LINUX_XLIST_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <linux/stddef.h>
 | 
					 | 
				
			||||||
#include <linux/poison.h>
 | 
					 | 
				
			||||||
#include <linux/prefetch.h>
 | 
					 | 
				
			||||||
#include <asm/system.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct xlist_head {
 | 
					 | 
				
			||||||
	struct xlist_head *next;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline void INIT_XLIST_HEAD(struct xlist_head *list)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	list->next = NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline int xlist_empty(struct xlist_head *head)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return head->next == NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline void xlist_add(struct xlist_head *new, struct xlist_head *tail,
 | 
					 | 
				
			||||||
			     struct xlist_head *head)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct xlist_head *cur;
 | 
					 | 
				
			||||||
	struct xlist_head *check;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	while (1) {
 | 
					 | 
				
			||||||
		cur = head->next;
 | 
					 | 
				
			||||||
		tail->next = cur;
 | 
					 | 
				
			||||||
		check = cmpxchg(&head->next, cur, new);
 | 
					 | 
				
			||||||
		if (check == cur)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline struct xlist_head *xlist_del_head(struct xlist_head *head)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct xlist_head *cur;
 | 
					 | 
				
			||||||
	struct xlist_head *check;
 | 
					 | 
				
			||||||
	struct xlist_head *next;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	while (1) {
 | 
					 | 
				
			||||||
		cur = head->next;
 | 
					 | 
				
			||||||
		if (!cur)
 | 
					 | 
				
			||||||
			goto out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		next = cur->next;
 | 
					 | 
				
			||||||
		check = cmpxchg(&head->next, cur, next);
 | 
					 | 
				
			||||||
		if (check == cur)
 | 
					 | 
				
			||||||
			goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
out:
 | 
					 | 
				
			||||||
	return cur;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline struct xlist_head *xlist_del_head_fast(struct xlist_head *head)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct xlist_head *cur;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cur = head->next;
 | 
					 | 
				
			||||||
	if (!cur)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	head->next = cur->next;
 | 
					 | 
				
			||||||
	return cur;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline void xlist_splice(struct xlist_head *list,
 | 
					 | 
				
			||||||
				struct xlist_head *head)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct xlist_head *cur;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	WARN_ON(head->next);
 | 
					 | 
				
			||||||
	cur = xchg(&list->next, NULL);
 | 
					 | 
				
			||||||
	head->next = cur;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
		Loading…
	
		Reference in a new issue