4 * Userspace RCU library - x86 compatibility checks
6 * Copyright (c) 2009 Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
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_arch.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.
125 __asm__
__volatile__("ud2");
127 mutex_lock_signal_restore(&compat_mutex
, &mask
);
131 unsigned long _compat_uatomic_xchg(void *addr
, unsigned long _new
, int len
)
134 unsigned long retval
;
136 mutex_lock_signal_save(&compat_mutex
, &mask
);
139 retval
= *(unsigned char *)addr
;
140 *(unsigned char *)addr
= (unsigned char)_new
;
143 retval
= *(unsigned short *)addr
;
144 *(unsigned short *)addr
= (unsigned short)_new
;
147 retval
= *(unsigned int *)addr
;
148 *(unsigned int *)addr
= (unsigned int)_new
;
152 * generate an illegal instruction. Cannot catch this with
153 * linker tricks when optimizations are disabled.
155 __asm__
__volatile__("ud2");
157 mutex_lock_signal_restore(&compat_mutex
, &mask
);
161 unsigned long _compat_uatomic_cmpxchg(void *addr
, unsigned long old
,
162 unsigned long _new
, int len
)
164 unsigned long retval
;
167 mutex_lock_signal_save(&compat_mutex
, &mask
);
171 unsigned char result
= *(unsigned char *)addr
;
172 if (result
== (unsigned char)old
)
173 *(unsigned char *)addr
= (unsigned char)_new
;
179 unsigned short result
= *(unsigned short *)addr
;
180 if (result
== (unsigned short)old
)
181 *(unsigned short *)addr
= (unsigned short)_new
;
187 unsigned int result
= *(unsigned int *)addr
;
188 if (result
== (unsigned int)old
)
189 *(unsigned int *)addr
= (unsigned int)_new
;
195 * generate an illegal instruction. Cannot catch this with
196 * linker tricks when optimizations are disabled.
198 __asm__
__volatile__("ud2");
200 mutex_lock_signal_restore(&compat_mutex
, &mask
);
204 unsigned long _compat_uatomic_add_return(void *addr
, unsigned long v
, int len
)
207 unsigned long result
;
209 mutex_lock_signal_save(&compat_mutex
, &mask
);
212 *(unsigned char *)addr
+= (unsigned char)v
;
213 result
= *(unsigned char *)addr
;
216 *(unsigned short *)addr
+= (unsigned short)v
;
217 result
= *(unsigned short *)addr
;
220 *(unsigned int *)addr
+= (unsigned int)v
;
221 result
= *(unsigned int *)addr
;
225 * generate an illegal instruction. Cannot catch this with
226 * linker tricks when optimizations are disabled.
228 __asm__
__volatile__("ud2");
230 mutex_lock_signal_restore(&compat_mutex
, &mask
);
234 int __rcu_cas_init(void)
236 if (__rcu_cas_avail
< 0)
237 __rcu_cas_avail
= compare_and_swap_is_available();
238 return __rcu_cas_avail
;