Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

um: add a UML specific futex implementation

The generic asm futex implementation emulates atomic access to
memory by doing a get_user followed by put_user. These translate
to two mapping operations on UML with paging enabled in the
meantime. This, in turn may end up changing interrupts,
invoking the signal loop, etc.

This replaces the generic implementation by a mapping followed
by an operation on the mapped segment.

Signed-off-by: Anton Ivanov <anton.ivanov@cambridgegreys.com>
Signed-off-by: Richard Weinberger <richard@nod.at>

authored by

Anton Ivanov and committed by
Richard Weinberger
dd3035a2 c0ecca66

+150 -1
-1
arch/um/include/asm/Kbuild
··· 9 9 generic-y += extable.h 10 10 generic-y += fb.h 11 11 generic-y += ftrace.h 12 - generic-y += futex.h 13 12 generic-y += hw_irq.h 14 13 generic-y += irq_regs.h 15 14 generic-y += irq_work.h
+14
arch/um/include/asm/futex.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _ASM_UM_FUTEX_H 3 + #define _ASM_UM_FUTEX_H 4 + 5 + #include <linux/futex.h> 6 + #include <linux/uaccess.h> 7 + #include <asm/errno.h> 8 + 9 + 10 + int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr); 11 + int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, 12 + u32 oldval, u32 newval); 13 + 14 + #endif
+136
arch/um/kernel/skas/uaccess.c
··· 11 11 #include <asm/current.h> 12 12 #include <asm/page.h> 13 13 #include <kern_util.h> 14 + #include <asm/futex.h> 14 15 #include <os.h> 15 16 16 17 pte_t *virt_to_pte(struct mm_struct *mm, unsigned long addr) ··· 249 248 return 0; 250 249 } 251 250 EXPORT_SYMBOL(__strnlen_user); 251 + 252 + /** 253 + * arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant 254 + * argument and comparison of the previous 255 + * futex value with another constant. 256 + * 257 + * @encoded_op: encoded operation to execute 258 + * @uaddr: pointer to user space address 259 + * 260 + * Return: 261 + * 0 - On success 262 + * -EFAULT - User access resulted in a page fault 263 + * -EAGAIN - Atomic operation was unable to complete due to contention 264 + * -ENOSYS - Operation not supported 265 + */ 266 + 267 + int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr) 268 + { 269 + int oldval, ret; 270 + struct page *page; 271 + unsigned long addr = (unsigned long) uaddr; 272 + pte_t *pte; 273 + 274 + ret = -EFAULT; 275 + if (!access_ok(uaddr, sizeof(*uaddr))) 276 + return -EFAULT; 277 + preempt_disable(); 278 + pte = maybe_map(addr, 1); 279 + if (pte == NULL) 280 + goto out_inuser; 281 + 282 + page = pte_page(*pte); 283 + #ifdef CONFIG_64BIT 284 + pagefault_disable(); 285 + addr = (unsigned long) page_address(page) + 286 + (((unsigned long) addr) & ~PAGE_MASK); 287 + #else 288 + addr = (unsigned long) kmap_atomic(page) + 289 + ((unsigned long) addr & ~PAGE_MASK); 290 + #endif 291 + uaddr = (u32 *) addr; 292 + oldval = *uaddr; 293 + 294 + ret = 0; 295 + 296 + switch (op) { 297 + case FUTEX_OP_SET: 298 + *uaddr = oparg; 299 + break; 300 + case FUTEX_OP_ADD: 301 + *uaddr += oparg; 302 + break; 303 + case FUTEX_OP_OR: 304 + *uaddr |= oparg; 305 + break; 306 + case FUTEX_OP_ANDN: 307 + *uaddr &= ~oparg; 308 + break; 309 + case FUTEX_OP_XOR: 310 + *uaddr ^= oparg; 311 + break; 312 + default: 313 + ret = -ENOSYS; 314 + } 315 + #ifdef CONFIG_64BIT 316 + pagefault_enable(); 317 + #else 318 + kunmap_atomic((void *)addr); 319 + #endif 320 + 321 + out_inuser: 322 + preempt_enable(); 323 + 324 + if (ret == 0) 325 + *oval = oldval; 326 + 327 + return ret; 328 + } 329 + EXPORT_SYMBOL(arch_futex_atomic_op_inuser); 330 + 331 + /** 332 + * futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the 333 + * uaddr with newval if the current value is 334 + * oldval. 335 + * @uval: pointer to store content of @uaddr 336 + * @uaddr: pointer to user space address 337 + * @oldval: old value 338 + * @newval: new value to store to @uaddr 339 + * 340 + * Return: 341 + * 0 - On success 342 + * -EFAULT - User access resulted in a page fault 343 + * -EAGAIN - Atomic operation was unable to complete due to contention 344 + * -ENOSYS - Function not implemented (only if !HAVE_FUTEX_CMPXCHG) 345 + */ 346 + 347 + int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, 348 + u32 oldval, u32 newval) 349 + { 350 + struct page *page; 351 + pte_t *pte; 352 + int ret = -EFAULT; 353 + 354 + if (!access_ok(uaddr, sizeof(*uaddr))) 355 + return -EFAULT; 356 + 357 + preempt_disable(); 358 + pte = maybe_map((unsigned long) uaddr, 1); 359 + if (pte == NULL) 360 + goto out_inatomic; 361 + 362 + page = pte_page(*pte); 363 + #ifdef CONFIG_64BIT 364 + pagefault_disable(); 365 + uaddr = page_address(page) + (((unsigned long) uaddr) & ~PAGE_MASK); 366 + #else 367 + uaddr = kmap_atomic(page) + ((unsigned long) uaddr & ~PAGE_MASK); 368 + #endif 369 + 370 + *uval = *uaddr; 371 + 372 + ret = cmpxchg(uaddr, oldval, newval); 373 + 374 + #ifdef CONFIG_64BIT 375 + pagefault_enable(); 376 + #else 377 + kunmap_atomic(uaddr); 378 + #endif 379 + ret = 0; 380 + 381 + out_inatomic: 382 + preempt_enable(); 383 + return ret; 384 + } 385 + EXPORT_SYMBOL(futex_atomic_cmpxchg_inatomic);