diff options
| -rw-r--r-- | ipc/util.c | 103 | 
1 files changed, 16 insertions, 87 deletions
diff --git a/ipc/util.c b/ipc/util.c index abfc13e8677..809ec5ec812 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -466,51 +466,13 @@ void ipc_free(void* ptr, int size)  		kfree(ptr);  } -/* - * rcu allocations: - * There are three headers that are prepended to the actual allocation: - * - during use: ipc_rcu_hdr. - * - during the rcu grace period: ipc_rcu_grace. - * - [only if vmalloc]: ipc_rcu_sched. - * Their lifetime doesn't overlap, thus the headers share the same memory. - * Unlike a normal union, they are right-aligned, thus some container_of - * forward/backward casting is necessary: - */ -struct ipc_rcu_hdr -{ -	atomic_t refcount; -	int is_vmalloc; -	void *data[0]; -}; - - -struct ipc_rcu_grace -{ +struct ipc_rcu {  	struct rcu_head rcu; +	atomic_t refcount;  	/* "void *" makes sure alignment of following data is sane. */  	void *data[0];  }; -struct ipc_rcu_sched -{ -	struct work_struct work; -	/* "void *" makes sure alignment of following data is sane. */ -	void *data[0]; -}; - -#define HDRLEN_KMALLOC		(sizeof(struct ipc_rcu_grace) > sizeof(struct ipc_rcu_hdr) ? \ -					sizeof(struct ipc_rcu_grace) : sizeof(struct ipc_rcu_hdr)) -#define HDRLEN_VMALLOC		(sizeof(struct ipc_rcu_sched) > HDRLEN_KMALLOC ? \ -					sizeof(struct ipc_rcu_sched) : HDRLEN_KMALLOC) - -static inline int rcu_use_vmalloc(int size) -{ -	/* Too big for a single page? */ -	if (HDRLEN_KMALLOC + size > PAGE_SIZE) -		return 1; -	return 0; -} -  /**   *	ipc_rcu_alloc	-	allocate ipc and rcu space    *	@size: size desired @@ -520,74 +482,41 @@ static inline int rcu_use_vmalloc(int size)   */  void *ipc_rcu_alloc(int size)  { -	void *out; -  	/* -	 * We prepend the allocation with the rcu struct, and -	 * workqueue if necessary (for vmalloc). +	 * We prepend the allocation with the rcu struct  	 */ -	if (rcu_use_vmalloc(size)) { -		out = vmalloc(HDRLEN_VMALLOC + size); -		if (!out) -			goto done; - -		out += HDRLEN_VMALLOC; -		container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 1; -	} else { -		out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL); -		if (!out) -			goto done; - -		out += HDRLEN_KMALLOC; -		container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 0; -	} - -	/* set reference counter no matter what kind of allocation was done */ -	atomic_set(&container_of(out, struct ipc_rcu_hdr, data)->refcount, 1); -done: -	return out; +	struct ipc_rcu *out = ipc_alloc(sizeof(struct ipc_rcu) + size); +	if (unlikely(!out)) +		return NULL; +	atomic_set(&out->refcount, 1); +	return out->data;  }  int ipc_rcu_getref(void *ptr)  { -	return atomic_inc_not_zero(&container_of(ptr, struct ipc_rcu_hdr, data)->refcount); -} - -static void ipc_do_vfree(struct work_struct *work) -{ -	vfree(container_of(work, struct ipc_rcu_sched, work)); +	return atomic_inc_not_zero(&container_of(ptr, struct ipc_rcu, data)->refcount);  }  /**   * ipc_schedule_free - free ipc + rcu space   * @head: RCU callback structure for queued work - *  - * Since RCU callback function is called in bh, - * we need to defer the vfree to schedule_work().   */  static void ipc_schedule_free(struct rcu_head *head)  { -	struct ipc_rcu_grace *grace; -	struct ipc_rcu_sched *sched; - -	grace = container_of(head, struct ipc_rcu_grace, rcu); -	sched = container_of(&(grace->data[0]), struct ipc_rcu_sched, -				data[0]); - -	INIT_WORK(&sched->work, ipc_do_vfree); -	schedule_work(&sched->work); +	vfree(container_of(head, struct ipc_rcu, rcu));  }  void ipc_rcu_putref(void *ptr)  { -	if (!atomic_dec_and_test(&container_of(ptr, struct ipc_rcu_hdr, data)->refcount)) +	struct ipc_rcu *p = container_of(ptr, struct ipc_rcu, data); + +	if (!atomic_dec_and_test(&p->refcount))  		return; -	if (container_of(ptr, struct ipc_rcu_hdr, data)->is_vmalloc) { -		call_rcu(&container_of(ptr, struct ipc_rcu_grace, data)->rcu, -				ipc_schedule_free); +	if (is_vmalloc_addr(ptr)) { +		call_rcu(&p->rcu, ipc_schedule_free);  	} else { -		kfree_rcu(container_of(ptr, struct ipc_rcu_grace, data), rcu); +		kfree_rcu(p, rcu);  	}  }  |