futex.h 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. #ifndef _ASM_PARISC_FUTEX_H
  3. #define _ASM_PARISC_FUTEX_H
  4. #ifdef __KERNEL__
  5. #include <linux/futex.h>
  6. #include <linux/uaccess.h>
  7. #include <asm/atomic.h>
  8. #include <asm/errno.h>
  9. /* The following has to match the LWS code in syscall.S. We have
  10. sixteen four-word locks. */
  11. static inline void
  12. _futex_spin_lock_irqsave(u32 __user *uaddr, unsigned long int *flags)
  13. {
  14. extern u32 lws_lock_start[];
  15. long index = ((long)uaddr & 0xf0) >> 2;
  16. arch_spinlock_t *s = (arch_spinlock_t *)&lws_lock_start[index];
  17. local_irq_save(*flags);
  18. arch_spin_lock(s);
  19. }
  20. static inline void
  21. _futex_spin_unlock_irqrestore(u32 __user *uaddr, unsigned long int *flags)
  22. {
  23. extern u32 lws_lock_start[];
  24. long index = ((long)uaddr & 0xf0) >> 2;
  25. arch_spinlock_t *s = (arch_spinlock_t *)&lws_lock_start[index];
  26. arch_spin_unlock(s);
  27. local_irq_restore(*flags);
  28. }
  29. static inline int
  30. arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
  31. {
  32. unsigned long int flags;
  33. int oldval, ret;
  34. u32 tmp;
  35. _futex_spin_lock_irqsave(uaddr, &flags);
  36. pagefault_disable();
  37. ret = -EFAULT;
  38. if (unlikely(get_user(oldval, uaddr) != 0))
  39. goto out_pagefault_enable;
  40. ret = 0;
  41. tmp = oldval;
  42. switch (op) {
  43. case FUTEX_OP_SET:
  44. tmp = oparg;
  45. break;
  46. case FUTEX_OP_ADD:
  47. tmp += oparg;
  48. break;
  49. case FUTEX_OP_OR:
  50. tmp |= oparg;
  51. break;
  52. case FUTEX_OP_ANDN:
  53. tmp &= ~oparg;
  54. break;
  55. case FUTEX_OP_XOR:
  56. tmp ^= oparg;
  57. break;
  58. default:
  59. ret = -ENOSYS;
  60. }
  61. if (ret == 0 && unlikely(put_user(tmp, uaddr) != 0))
  62. ret = -EFAULT;
  63. out_pagefault_enable:
  64. pagefault_enable();
  65. _futex_spin_unlock_irqrestore(uaddr, &flags);
  66. if (!ret)
  67. *oval = oldval;
  68. return ret;
  69. }
  70. static inline int
  71. futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
  72. u32 oldval, u32 newval)
  73. {
  74. u32 val;
  75. unsigned long flags;
  76. /* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is
  77. * our gateway page, and causes no end of trouble...
  78. */
  79. if (uaccess_kernel() && !uaddr)
  80. return -EFAULT;
  81. if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
  82. return -EFAULT;
  83. /* HPPA has no cmpxchg in hardware and therefore the
  84. * best we can do here is use an array of locks. The
  85. * lock selected is based on a hash of the userspace
  86. * address. This should scale to a couple of CPUs.
  87. */
  88. _futex_spin_lock_irqsave(uaddr, &flags);
  89. if (unlikely(get_user(val, uaddr) != 0)) {
  90. _futex_spin_unlock_irqrestore(uaddr, &flags);
  91. return -EFAULT;
  92. }
  93. if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) {
  94. _futex_spin_unlock_irqrestore(uaddr, &flags);
  95. return -EFAULT;
  96. }
  97. *uval = val;
  98. _futex_spin_unlock_irqrestore(uaddr, &flags);
  99. return 0;
  100. }
  101. #endif /*__KERNEL__*/
  102. #endif /*_ASM_PARISC_FUTEX_H*/