| 
									
										
										
										
											2006-01-09 15:59:18 -08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Assembly implementation of the mutex fastpath, based on atomic | 
					
						
							|  |  |  |  * decrement/increment. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * started by Ingo Molnar: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Copyright (C) 2004, 2005, 2006 Red Hat, Inc., Ingo Molnar <mingo@redhat.com> | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-10-22 22:26:29 -07:00
										 |  |  | #ifndef _ASM_X86_MUTEX_32_H
 | 
					
						
							|  |  |  | #define _ASM_X86_MUTEX_32_H
 | 
					
						
							| 
									
										
										
										
											2006-01-09 15:59:18 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-03-04 16:46:27 -08:00
										 |  |  | #include <asm/alternative.h>
 | 
					
						
							| 
									
										
										
										
											2006-03-23 02:59:32 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-09 15:59:18 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  *  __mutex_fastpath_lock - try to take the lock by moving the count | 
					
						
							|  |  |  |  *                          from 1 to a 0 value | 
					
						
							|  |  |  |  *  @count: pointer of type atomic_t | 
					
						
							|  |  |  |  *  @fn: function to call if the original value was not 1 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Change the count from 1 to a value lower than 1, and call <fn> if it | 
					
						
							|  |  |  |  * wasn't 1 originally. This function MUST leave the value lower than 1 | 
					
						
							|  |  |  |  * even when the "1" assertion wasn't true. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-03-23 01:02:53 -07:00
										 |  |  | #define __mutex_fastpath_lock(count, fail_fn)			\
 | 
					
						
							|  |  |  | do {								\ | 
					
						
							|  |  |  | 	unsigned int dummy;					\ | 
					
						
							|  |  |  | 								\ | 
					
						
							|  |  |  | 	typecheck(atomic_t *, count);				\ | 
					
						
							| 
									
										
										
										
											2008-01-30 13:31:17 +01:00
										 |  |  | 	typecheck_fn(void (*)(atomic_t *), fail_fn);		\ | 
					
						
							| 
									
										
										
										
											2008-03-23 01:02:53 -07:00
										 |  |  | 								\ | 
					
						
							|  |  |  | 	asm volatile(LOCK_PREFIX "   decl (%%eax)\n"		\ | 
					
						
							|  |  |  | 		     "   jns 1f	\n"				\ | 
					
						
							|  |  |  | 		     "   call " #fail_fn "\n"			\ | 
					
						
							|  |  |  | 		     "1:\n"					\ | 
					
						
							|  |  |  | 		     : "=a" (dummy)				\ | 
					
						
							|  |  |  | 		     : "a" (count)				\ | 
					
						
							|  |  |  | 		     : "memory", "ecx", "edx");			\ | 
					
						
							| 
									
										
										
										
											2006-01-09 15:59:18 -08:00
										 |  |  | } while (0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  *  __mutex_fastpath_lock_retval - try to take the lock by moving the count | 
					
						
							|  |  |  |  *                                 from 1 to a 0 value | 
					
						
							|  |  |  |  *  @count: pointer of type atomic_t | 
					
						
							|  |  |  |  *  @fail_fn: function to call if the original value was not 1 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Change the count from 1 to a value lower than 1, and call <fail_fn> if it | 
					
						
							|  |  |  |  * wasn't 1 originally. This function returns 0 if the fastpath succeeds, | 
					
						
							|  |  |  |  * or anything the slow path function returns | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-03-23 01:02:53 -07:00
										 |  |  | static inline int __mutex_fastpath_lock_retval(atomic_t *count, | 
					
						
							|  |  |  | 					       int (*fail_fn)(atomic_t *)) | 
					
						
							| 
									
										
										
										
											2006-01-09 15:59:18 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (unlikely(atomic_dec_return(count) < 0)) | 
					
						
							|  |  |  | 		return fail_fn(count); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  *  __mutex_fastpath_unlock - try to promote the mutex from 0 to 1 | 
					
						
							|  |  |  |  *  @count: pointer of type atomic_t | 
					
						
							|  |  |  |  *  @fail_fn: function to call if the original value was not 0 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * try to promote the mutex from 0 to 1. if it wasn't 0, call <fail_fn>. | 
					
						
							|  |  |  |  * In the failure case, this function is allowed to either set the value | 
					
						
							|  |  |  |  * to 1, or to set it to a value lower than 1. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * If the implementation sets it to a value of lower than 1, the | 
					
						
							|  |  |  |  * __mutex_slowpath_needs_to_unlock() macro needs to return 1, it needs | 
					
						
							|  |  |  |  * to return 0 otherwise. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-03-23 01:02:53 -07:00
										 |  |  | #define __mutex_fastpath_unlock(count, fail_fn)			\
 | 
					
						
							|  |  |  | do {								\ | 
					
						
							|  |  |  | 	unsigned int dummy;					\ | 
					
						
							|  |  |  | 								\ | 
					
						
							|  |  |  | 	typecheck(atomic_t *, count);				\ | 
					
						
							| 
									
										
										
										
											2008-01-30 13:31:17 +01:00
										 |  |  | 	typecheck_fn(void (*)(atomic_t *), fail_fn);		\ | 
					
						
							| 
									
										
										
										
											2008-03-23 01:02:53 -07:00
										 |  |  | 								\ | 
					
						
							|  |  |  | 	asm volatile(LOCK_PREFIX "   incl (%%eax)\n"		\ | 
					
						
							|  |  |  | 		     "   jg	1f\n"				\ | 
					
						
							|  |  |  | 		     "   call " #fail_fn "\n"			\ | 
					
						
							|  |  |  | 		     "1:\n"					\ | 
					
						
							|  |  |  | 		     : "=a" (dummy)				\ | 
					
						
							|  |  |  | 		     : "a" (count)				\ | 
					
						
							|  |  |  | 		     : "memory", "ecx", "edx");			\ | 
					
						
							| 
									
										
										
										
											2006-01-09 15:59:18 -08:00
										 |  |  | } while (0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define __mutex_slowpath_needs_to_unlock()	1
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * __mutex_fastpath_trylock - try to acquire the mutex, without waiting | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  @count: pointer of type atomic_t | 
					
						
							|  |  |  |  *  @fail_fn: fallback function | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Change the count from 1 to a value lower than 1, and return 0 (failure) | 
					
						
							|  |  |  |  * if it wasn't 1 originally, or return 1 (success) otherwise. This function | 
					
						
							|  |  |  |  * MUST leave the value lower than 1 even when the "1" assertion wasn't true. | 
					
						
							|  |  |  |  * Additionally, if the value was < 0 originally, this function must not leave | 
					
						
							|  |  |  |  * it to 0 on failure. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-03-23 01:02:53 -07:00
										 |  |  | static inline int __mutex_fastpath_trylock(atomic_t *count, | 
					
						
							|  |  |  | 					   int (*fail_fn)(atomic_t *)) | 
					
						
							| 
									
										
										
										
											2006-01-09 15:59:18 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * We have two variants here. The cmpxchg based one is the best one | 
					
						
							|  |  |  | 	 * because it never induce a false contention state.  It is included | 
					
						
							|  |  |  | 	 * here because architectures using the inc/dec algorithms over the | 
					
						
							|  |  |  | 	 * xchg ones are much more likely to support cmpxchg natively. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * If not we fall back to the spinlock based variant - that is | 
					
						
							|  |  |  | 	 * just as efficient (and simpler) as a 'destructive' probing of | 
					
						
							|  |  |  | 	 * the mutex state would be. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | #ifdef __HAVE_ARCH_CMPXCHG
 | 
					
						
							| 
									
										
										
										
											2006-01-11 15:50:47 -08:00
										 |  |  | 	if (likely(atomic_cmpxchg(count, 1, 0) == 1)) | 
					
						
							| 
									
										
										
										
											2006-01-09 15:59:18 -08:00
										 |  |  | 		return 1; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 	return fail_fn(count); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-10-22 22:26:29 -07:00
										 |  |  | #endif /* _ASM_X86_MUTEX_32_H */
 |