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 * Using attribute "weak" for __rcu_cas_avail and
31 * __urcu_x86_compat_mutex. Those are globally visible by the entire
32 * program, even though many shared objects may have their own version.
33 * The first version that gets loaded will be used by the entire
34 * program (executable and all shared objects).
38 * It does not really matter if the constructor is called before using
39 * the library, as long as the caller checks if __rcu_cas_avail < 0 and calls
40 * compat_arch_init() explicitely if needed.
42 int __attribute__((constructor
)) __rcu_cas_init(void);
50 int __rcu_cas_avail
= -1;
53 pthread_mutex_t __urcu_x86_compat_mutex
= PTHREAD_MUTEX_INITIALIZER
;
56 * get_eflags/set_eflags/compare_and_swap_is_available imported from glibc
57 * 2.3.5. linuxthreads/sysdeps/i386/pt-machine.h.
60 static int get_eflags (void)
63 __asm__
__volatile__ ("pushfl; popl %0" : "=r" (res
) : );
67 static void set_eflags (int newflags
)
69 __asm__
__volatile__ ("pushl %0; popfl" : : "r" (newflags
) : "cc");
72 static int compare_and_swap_is_available (void)
74 int oldflags
= get_eflags ();
76 /* Flip AC bit in EFLAGS. */
77 set_eflags (oldflags
^ 0x40000);
78 /* See if bit changed. */
79 changed
= (get_eflags () ^ oldflags
) & 0x40000;
81 set_eflags (oldflags
);
82 /* If the AC flag did not change, it's a 386 and it lacks cmpxchg.
83 Otherwise, it's a 486 or above and it has cmpxchg. */
87 static void mutex_lock_signal_save(pthread_mutex_t
*mutex
, sigset_t
*oldmask
)
93 ret
= sigfillset(&newmask
);
95 ret
= pthread_sigmask(SIG_BLOCK
, &newmask
, oldmask
);
97 ret
= pthread_mutex_lock(&__urcu_x86_compat_mutex
);
101 static void mutex_lock_signal_restore(pthread_mutex_t
*mutex
, sigset_t
*oldmask
)
105 ret
= pthread_mutex_unlock(&__urcu_x86_compat_mutex
);
107 ret
= pthread_sigmask(SIG_SETMASK
, oldmask
, NULL
);
111 unsigned long _compat_uatomic_set(void *addr
, unsigned long _new
, int len
)
114 unsigned long result
;
116 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
119 *(unsigned char *)addr
= (unsigned char)_new
;
120 result
= *(unsigned char *)addr
;
123 *(unsigned short *)addr
= (unsigned short)_new
;
124 result
= *(unsigned short *)addr
;
127 *(unsigned int *)addr
= (unsigned int)_new
;
128 result
= *(unsigned int *)addr
;
132 * generate an illegal instruction. Cannot catch this with
133 * linker tricks when optimizations are disabled.
136 __asm__
__volatile__("ud2");
138 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
142 unsigned long _compat_uatomic_xchg(void *addr
, unsigned long _new
, int len
)
145 unsigned long retval
;
147 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
150 retval
= *(unsigned char *)addr
;
151 *(unsigned char *)addr
= (unsigned char)_new
;
154 retval
= *(unsigned short *)addr
;
155 *(unsigned short *)addr
= (unsigned short)_new
;
158 retval
= *(unsigned int *)addr
;
159 *(unsigned int *)addr
= (unsigned int)_new
;
163 * generate an illegal instruction. Cannot catch this with
164 * linker tricks when optimizations are disabled.
166 retval
= 0; /* silence gcc warnings */
167 __asm__
__volatile__("ud2");
169 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
173 unsigned long _compat_uatomic_cmpxchg(void *addr
, unsigned long old
,
174 unsigned long _new
, int len
)
176 unsigned long retval
;
179 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
183 unsigned char result
= *(unsigned char *)addr
;
184 if (result
== (unsigned char)old
)
185 *(unsigned char *)addr
= (unsigned char)_new
;
191 unsigned short result
= *(unsigned short *)addr
;
192 if (result
== (unsigned short)old
)
193 *(unsigned short *)addr
= (unsigned short)_new
;
199 unsigned int result
= *(unsigned int *)addr
;
200 if (result
== (unsigned int)old
)
201 *(unsigned int *)addr
= (unsigned int)_new
;
207 * generate an illegal instruction. Cannot catch this with
208 * linker tricks when optimizations are disabled.
210 retval
= 0; /* silence gcc warnings */
211 __asm__
__volatile__("ud2");
213 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
217 void _compat_uatomic_or(void *addr
, unsigned long v
, int len
)
221 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
224 *(unsigned char *)addr
|= (unsigned char)v
;
227 *(unsigned short *)addr
|= (unsigned short)v
;
230 *(unsigned int *)addr
|= (unsigned int)v
;
234 * generate an illegal instruction. Cannot catch this with
235 * linker tricks when optimizations are disabled.
237 __asm__
__volatile__("ud2");
239 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
242 void _compat_uatomic_and(void *addr
, unsigned long v
, int len
)
246 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
249 *(unsigned char *)addr
&= (unsigned char)v
;
252 *(unsigned short *)addr
&= (unsigned short)v
;
255 *(unsigned int *)addr
&= (unsigned int)v
;
259 * generate an illegal instruction. Cannot catch this with
260 * linker tricks when optimizations are disabled.
262 __asm__
__volatile__("ud2");
264 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
267 unsigned long _compat_uatomic_add_return(void *addr
, unsigned long v
, int len
)
270 unsigned long result
;
272 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
275 *(unsigned char *)addr
+= (unsigned char)v
;
276 result
= *(unsigned char *)addr
;
279 *(unsigned short *)addr
+= (unsigned short)v
;
280 result
= *(unsigned short *)addr
;
283 *(unsigned int *)addr
+= (unsigned int)v
;
284 result
= *(unsigned int *)addr
;
288 * generate an illegal instruction. Cannot catch this with
289 * linker tricks when optimizations are disabled.
291 result
= 0; /* silence gcc warnings */
292 __asm__
__volatile__("ud2");
294 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
298 int __rcu_cas_init(void)
300 if (__rcu_cas_avail
< 0)
301 __rcu_cas_avail
= compare_and_swap_is_available();
302 return __rcu_cas_avail
;