1/* 2 * Copyright 2010 Tilera Corporation. All Rights Reserved. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation, version 2. 7 * 8 * This program is distributed in the hope that it will be useful, but 9 * WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 11 * NON INFRINGEMENT. See the GNU General Public License for 12 * more details. 13 * 14 * These routines make two important assumptions: 15 * 16 * 1. atomic_t is really an int and can be freely cast back and forth 17 * (validated in __init_atomic_per_cpu). 18 * 19 * 2. userspace uses sys_cmpxchg() for all atomic operations, thus using 20 * the same locking convention that all the kernel atomic routines use. 21 */ 22 23#ifndef _ASM_TILE_FUTEX_H 24#define _ASM_TILE_FUTEX_H 25 26#ifndef __ASSEMBLY__ 27 28#include <linux/futex.h> 29#include <linux/uaccess.h> 30#include <linux/errno.h> 31 32extern struct __get_user futex_set(u32 __user *v, int i); 33extern struct __get_user futex_add(u32 __user *v, int n); 34extern struct __get_user futex_or(u32 __user *v, int n); 35extern struct __get_user futex_andn(u32 __user *v, int n); 36extern struct __get_user futex_cmpxchg(u32 __user *v, int o, int n); 37 38#ifndef __tilegx__ 39extern struct __get_user futex_xor(u32 __user *v, int n); 40#else 41static inline struct __get_user futex_xor(u32 __user *uaddr, int n) 42{ 43 struct __get_user asm_ret = __get_user_4(uaddr); 44 if (!asm_ret.err) { 45 int oldval, newval; 46 do { 47 oldval = asm_ret.val; 48 newval = oldval ^ n; 49 asm_ret = futex_cmpxchg(uaddr, oldval, newval); 50 } while (asm_ret.err == 0 && oldval != asm_ret.val); 51 } 52 return asm_ret; 53} 54#endif 55 56static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) 57{ 58 int op = (encoded_op >> 28) & 7; 59 int cmp = (encoded_op >> 24) & 15; 60 int oparg = (encoded_op << 8) >> 20; 61 int cmparg = (encoded_op << 20) >> 20; 62 int ret; 63 struct __get_user asm_ret; 64 65 if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) 66 oparg = 1 << oparg; 67 68 if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) 69 return -EFAULT; 70 71 pagefault_disable(); 72 switch (op) { 73 case FUTEX_OP_SET: 74 asm_ret = futex_set(uaddr, oparg); 75 break; 76 case FUTEX_OP_ADD: 77 asm_ret = futex_add(uaddr, oparg); 78 break; 79 case FUTEX_OP_OR: 80 asm_ret = futex_or(uaddr, oparg); 81 break; 82 case FUTEX_OP_ANDN: 83 asm_ret = futex_andn(uaddr, oparg); 84 break; 85 case FUTEX_OP_XOR: 86 asm_ret = futex_xor(uaddr, oparg); 87 break; 88 default: 89 asm_ret.err = -ENOSYS; 90 } 91 pagefault_enable(); 92 93 ret = asm_ret.err; 94 95 if (!ret) { 96 switch (cmp) { 97 case FUTEX_OP_CMP_EQ: 98 ret = (asm_ret.val == cmparg); 99 break; 100 case FUTEX_OP_CMP_NE: 101 ret = (asm_ret.val != cmparg); 102 break; 103 case FUTEX_OP_CMP_LT: 104 ret = (asm_ret.val < cmparg); 105 break; 106 case FUTEX_OP_CMP_GE: 107 ret = (asm_ret.val >= cmparg); 108 break; 109 case FUTEX_OP_CMP_LE: 110 ret = (asm_ret.val <= cmparg); 111 break; 112 case FUTEX_OP_CMP_GT: 113 ret = (asm_ret.val > cmparg); 114 break; 115 default: 116 ret = -ENOSYS; 117 } 118 } 119 return ret; 120} 121 122static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, 123 u32 oldval, u32 newval) 124{ 125 struct __get_user asm_ret; 126 127 if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) 128 return -EFAULT; 129 130 asm_ret = futex_cmpxchg(uaddr, oldval, newval); 131 *uval = asm_ret.val; 132 return asm_ret.err; 133} 134 135#ifndef __tilegx__ 136/* Return failure from the atomic wrappers. */ 137struct __get_user __atomic_bad_address(int __user *addr); 138#endif 139 140#endif /* !__ASSEMBLY__ */ 141 142#endif /* _ASM_TILE_FUTEX_H */ 143