4 * Userspace RCU library - x86 compatibility checks
6 * Copyright (c) 2009 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 #include <urcu/uatomic.h>
30 * It does not really matter if the constructor is called before using
31 * the library, as long as the caller checks if __rcu_cas_avail < 0 and calls
32 * compat_arch_init() explicitely if needed.
34 int __attribute__((constructor
)) __rcu_cas_init(void);
41 int __rcu_cas_avail
= -1;
43 static pthread_mutex_t compat_mutex
= PTHREAD_MUTEX_INITIALIZER
;
46 * get_eflags/set_eflags/compare_and_swap_is_available imported from glibc
47 * 2.3.5. linuxthreads/sysdeps/i386/pt-machine.h.
50 static int get_eflags (void)
53 __asm__
__volatile__ ("pushfl; popl %0" : "=r" (res
) : );
57 static void set_eflags (int newflags
)
59 __asm__
__volatile__ ("pushl %0; popfl" : : "r" (newflags
) : "cc");
62 static int compare_and_swap_is_available (void)
64 int oldflags
= get_eflags ();
66 /* Flip AC bit in EFLAGS. */
67 set_eflags (oldflags
^ 0x40000);
68 /* See if bit changed. */
69 changed
= (get_eflags () ^ oldflags
) & 0x40000;
71 set_eflags (oldflags
);
72 /* If the AC flag did not change, it's a 386 and it lacks cmpxchg.
73 Otherwise, it's a 486 or above and it has cmpxchg. */
77 static void mutex_lock_signal_save(pthread_mutex_t
*mutex
, sigset_t
*oldmask
)
83 ret
= sigemptyset(&newmask
);
85 ret
= pthread_sigmask(SIG_SETMASK
, &newmask
, oldmask
);
87 ret
= pthread_mutex_lock(&compat_mutex
);
91 static void mutex_lock_signal_restore(pthread_mutex_t
*mutex
, sigset_t
*oldmask
)
95 ret
= pthread_mutex_unlock(&compat_mutex
);
97 ret
= pthread_sigmask(SIG_SETMASK
, oldmask
, NULL
);
101 unsigned long _compat_uatomic_set(void *addr
, unsigned long _new
, int len
)
104 unsigned long result
;
106 mutex_lock_signal_save(&compat_mutex
, &mask
);
109 *(unsigned char *)addr
= (unsigned char)_new
;
110 result
= *(unsigned char *)addr
;
113 *(unsigned short *)addr
= (unsigned short)_new
;
114 result
= *(unsigned short *)addr
;
117 *(unsigned int *)addr
= (unsigned int)_new
;
118 result
= *(unsigned int *)addr
;
122 * generate an illegal instruction. Cannot catch this with
123 * linker tricks when optimizations are disabled.
126 __asm__
__volatile__("ud2");
128 mutex_lock_signal_restore(&compat_mutex
, &mask
);
132 unsigned long _compat_uatomic_xchg(void *addr
, unsigned long _new
, int len
)
135 unsigned long retval
;
137 mutex_lock_signal_save(&compat_mutex
, &mask
);
140 retval
= *(unsigned char *)addr
;
141 *(unsigned char *)addr
= (unsigned char)_new
;
144 retval
= *(unsigned short *)addr
;
145 *(unsigned short *)addr
= (unsigned short)_new
;
148 retval
= *(unsigned int *)addr
;
149 *(unsigned int *)addr
= (unsigned int)_new
;
153 * generate an illegal instruction. Cannot catch this with
154 * linker tricks when optimizations are disabled.
156 retval
= 0; /* silence gcc warnings */
157 __asm__
__volatile__("ud2");
159 mutex_lock_signal_restore(&compat_mutex
, &mask
);
163 unsigned long _compat_uatomic_cmpxchg(void *addr
, unsigned long old
,
164 unsigned long _new
, int len
)
166 unsigned long retval
;
169 mutex_lock_signal_save(&compat_mutex
, &mask
);
173 unsigned char result
= *(unsigned char *)addr
;
174 if (result
== (unsigned char)old
)
175 *(unsigned char *)addr
= (unsigned char)_new
;
181 unsigned short result
= *(unsigned short *)addr
;
182 if (result
== (unsigned short)old
)
183 *(unsigned short *)addr
= (unsigned short)_new
;
189 unsigned int result
= *(unsigned int *)addr
;
190 if (result
== (unsigned int)old
)
191 *(unsigned int *)addr
= (unsigned int)_new
;
197 * generate an illegal instruction. Cannot catch this with
198 * linker tricks when optimizations are disabled.
200 retval
= 0; /* silence gcc warnings */
201 __asm__
__volatile__("ud2");
203 mutex_lock_signal_restore(&compat_mutex
, &mask
);
207 void _compat_uatomic_or(void *addr
, unsigned long v
, int len
)
211 mutex_lock_signal_save(&compat_mutex
, &mask
);
214 *(unsigned char *)addr
|= (unsigned char)v
;
217 *(unsigned short *)addr
|= (unsigned short)v
;
220 *(unsigned int *)addr
|= (unsigned int)v
;
224 * generate an illegal instruction. Cannot catch this with
225 * linker tricks when optimizations are disabled.
227 __asm__
__volatile__("ud2");
229 mutex_lock_signal_restore(&compat_mutex
, &mask
);
232 void _compat_uatomic_and(void *addr
, unsigned long v
, int len
)
236 mutex_lock_signal_save(&compat_mutex
, &mask
);
239 *(unsigned char *)addr
&= (unsigned char)v
;
242 *(unsigned short *)addr
&= (unsigned short)v
;
245 *(unsigned int *)addr
&= (unsigned int)v
;
249 * generate an illegal instruction. Cannot catch this with
250 * linker tricks when optimizations are disabled.
252 __asm__
__volatile__("ud2");
254 mutex_lock_signal_restore(&compat_mutex
, &mask
);
257 unsigned long _compat_uatomic_add_return(void *addr
, unsigned long v
, int len
)
260 unsigned long result
;
262 mutex_lock_signal_save(&compat_mutex
, &mask
);
265 *(unsigned char *)addr
+= (unsigned char)v
;
266 result
= *(unsigned char *)addr
;
269 *(unsigned short *)addr
+= (unsigned short)v
;
270 result
= *(unsigned short *)addr
;
273 *(unsigned int *)addr
+= (unsigned int)v
;
274 result
= *(unsigned int *)addr
;
278 * generate an illegal instruction. Cannot catch this with
279 * linker tricks when optimizations are disabled.
281 result
= 0; /* silence gcc warnings */
282 __asm__
__volatile__("ud2");
284 mutex_lock_signal_restore(&compat_mutex
, &mask
);
288 int __rcu_cas_init(void)
290 if (__rcu_cas_avail
< 0)
291 __rcu_cas_avail
= compare_and_swap_is_available();
292 return __rcu_cas_avail
;