Christian Svensson
2014-01-25 16:01:13 UTC
OpenRISC does not offer any way to do atomic operations in user space.
In order to provide atomic operations we need to implement these
operations in kernel space.
These operations disables premption by disabling interrupts while
executing their critical sections.
The following operations has been added:
SWAP: Atomically swap the values in pointers 1 and 2.
CMPXCHG: Writes new to *mem if *mem == old. Returns old *mem.
XCHG: Store NEW in *MEM and return the old value.
ADD: Add VAL to *MEM and return the old value of *MEM.
DECPOS: Decrement *MEM if it is > 0, and return the old value.
AND: Atomically *mem &= mask and return the old value of *mem.
OR: Atomically *mem |= mask and return the old value of *mem.
UMAX: If *mem < val, set *mem = max. Returns old value of *mem.
UMIN: If *mem > val, set *mem = min. Returns old value of *mem.
Signed-off-by: Christian Svensson <blue at cmd.nu>
---
arch/openrisc/kernel/entry.S | 181
++++++++++++++++++++++++++++++++++++++++--
1 file changed, 174 insertions(+), 7 deletions(-)
diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S
index fec8bf9..eafd2c0 100644
--- a/arch/openrisc/kernel/entry.S
+++ b/arch/openrisc/kernel/entry.S
@@ -1106,20 +1106,101 @@ ENTRY(sys_rt_sigreturn)
/* This is a catch-all syscall for atomic instructions for the OpenRISC
1000.
* The functions takes a variable number of parameters depending on which
- * particular flavour of atomic you want... parameter 1 is a flag
identifying
- * the atomic in question. Currently, this function implements the
- * following variants:
+ * particular flavour of atomic you want.
+ * Parameter 1 is a flag identifying the atomic in question.
*
- * XCHG:
+ * TODO: Flag value 0 can be used, and it is recommended that the next
+ * atomic instruction to be implemented uses it. Currently it is a no-op.
+ *
+ * Currently, this function implements the following variants:
+ *
+ * SWAP:
* @flag: 1
* @ptr1:
* @ptr2:
- * Atomically exchange the values in pointers 1 and 2.
+ * Atomically swap the values in pointers 1 and 2.
+ *
+ * CMPXCHG:
+ * @flag: 2
+ * @ptr: mem
+ * @val1: old
+ * @val2: new
+ * Writes new to *mem if *mem == old. Returns old *mem.
+ *
+ * XCHG:
+ * @flag: 3
+ * @ptr: mem
+ * @val1: new
+ * Store NEW in *MEM and return the old value.
+ *
+ * ADD:
+ * @flag: 4
+ * @ptr: mem
+ * @val1: val
+ * Add VAL to *MEM and return the old value of *MEM.
+ *
+ * DECPOS:
+ * @flag: 5
+ * @ptr: mem
+ * Decrement *MEM if it is > 0, and return the old value.
+ *
+ * AND:
+ * @flag: 6
+ * @ptr: mem
+ * @val1: mask
+ * Atomically *mem &= mask and return the old value of *mem.
+ *
+ * OR:
+ * @flag: 7
+ * @ptr: mem
+ * @val1: mask
+ * Atomically *mem |= mask and return the old value of *mem.
+ *
+ * UMAX: unsigned
+ * @flag: 8
+ * @ptr: mem
+ * @val1: max
+ * If *mem < val, set *mem = max. Returns old value of *mem.
+ *
+ * UMIN: unsigned
+ * @flag: 9
+ * @ptr: mem
+ * @val1: min
+ * If *mem > val, set *mem = min. Returns old value of *mem.
*
*/
-
ENTRY(sys_or1k_atomic)
- /* FIXME: This ignores r3 and always does an XCHG */
+ /* TODO: validate mem ptr(s) */
+ /* TODO: check bounds of argument */
+ l.movhi r19,hi(atomic_table)
+ l.ori r19,r19,lo(atomic_table)
+ l.slli r3,r3,3
+ l.add r19,r19,r3
+ l.jr r19
+ l.nop
+atomic_table:
+ l.jr r9
+ l.nop
+ l.j _atomic_swap
+ l.nop
+ l.j _atomic_cmpxchg
+ l.nop
+ l.j _atomic_xchg
+ l.nop
+ l.j _atomic_add
+ l.nop
+ l.j _atomic_decpos
+ l.nop
+ l.j _atomic_and
+ l.nop
+ l.j _atomic_or
+ l.nop
+ l.j _atomic_max
+ l.nop
+ l.j _atomic_min
+ l.nop
+
+_atomic_swap:
DISABLE_INTERRUPTS(r17,r19)
l.lwz r29,0(r4)
l.lwz r27,0(r5)
@@ -1129,4 +1210,90 @@ ENTRY(sys_or1k_atomic)
l.jr r9
l.or r11,r0,r0
+_atomic_cmpxchg:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.sfeq r29,r5
+ l.bnf _atomic_cmpxchg_done
+ l.nop
+
+ l.sw 0(r4),r6
+_atomic_cmpxchg_done:
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_xchg:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.sw 0(r4),r5
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_add:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.add r27,r29,r5
+ l.sw 0(r4),r27
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_decpos:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.sfgtsi r29,0
+ l.bnf _atomic_decpos_done
+ l.addi r27,r29,-1
+
+ l.sw 0(r4),r27
+_atomic_decpos_done:
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_and:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.and r27,r29,r5
+ l.sw 0(r4),r27
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_or:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.or r27,r29,r5
+ l.sw 0(r4),r27
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_max:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.sfltu r29,r5
+ l.bnf _atomic_max_done
+ l.nop
+
+ l.sw 0(r4),r5
+_atomic_max_done:
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_min:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.sfgtu r29,r5
+ l.bnf _atomic_min_done
+ l.nop
+
+ l.sw 0(r4),r5
+_atomic_min_done:
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
/* ============================================================[ EOF ]===
*/
--
1.7.10.4
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openrisc.net/pipermail/linux/attachments/20140125/703b3339/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-openrisc-Add-new-atomic-operations-to-or1k_atomic.patch
Type: application/octet-stream
Size: 5563 bytes
Desc: not available
URL: <http://lists.openrisc.net/pipermail/linux/attachments/20140125/703b3339/attachment-0001.obj>
In order to provide atomic operations we need to implement these
operations in kernel space.
These operations disables premption by disabling interrupts while
executing their critical sections.
The following operations has been added:
SWAP: Atomically swap the values in pointers 1 and 2.
CMPXCHG: Writes new to *mem if *mem == old. Returns old *mem.
XCHG: Store NEW in *MEM and return the old value.
ADD: Add VAL to *MEM and return the old value of *MEM.
DECPOS: Decrement *MEM if it is > 0, and return the old value.
AND: Atomically *mem &= mask and return the old value of *mem.
OR: Atomically *mem |= mask and return the old value of *mem.
UMAX: If *mem < val, set *mem = max. Returns old value of *mem.
UMIN: If *mem > val, set *mem = min. Returns old value of *mem.
Signed-off-by: Christian Svensson <blue at cmd.nu>
---
arch/openrisc/kernel/entry.S | 181
++++++++++++++++++++++++++++++++++++++++--
1 file changed, 174 insertions(+), 7 deletions(-)
diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S
index fec8bf9..eafd2c0 100644
--- a/arch/openrisc/kernel/entry.S
+++ b/arch/openrisc/kernel/entry.S
@@ -1106,20 +1106,101 @@ ENTRY(sys_rt_sigreturn)
/* This is a catch-all syscall for atomic instructions for the OpenRISC
1000.
* The functions takes a variable number of parameters depending on which
- * particular flavour of atomic you want... parameter 1 is a flag
identifying
- * the atomic in question. Currently, this function implements the
- * following variants:
+ * particular flavour of atomic you want.
+ * Parameter 1 is a flag identifying the atomic in question.
*
- * XCHG:
+ * TODO: Flag value 0 can be used, and it is recommended that the next
+ * atomic instruction to be implemented uses it. Currently it is a no-op.
+ *
+ * Currently, this function implements the following variants:
+ *
+ * SWAP:
* @flag: 1
* @ptr1:
* @ptr2:
- * Atomically exchange the values in pointers 1 and 2.
+ * Atomically swap the values in pointers 1 and 2.
+ *
+ * CMPXCHG:
+ * @flag: 2
+ * @ptr: mem
+ * @val1: old
+ * @val2: new
+ * Writes new to *mem if *mem == old. Returns old *mem.
+ *
+ * XCHG:
+ * @flag: 3
+ * @ptr: mem
+ * @val1: new
+ * Store NEW in *MEM and return the old value.
+ *
+ * ADD:
+ * @flag: 4
+ * @ptr: mem
+ * @val1: val
+ * Add VAL to *MEM and return the old value of *MEM.
+ *
+ * DECPOS:
+ * @flag: 5
+ * @ptr: mem
+ * Decrement *MEM if it is > 0, and return the old value.
+ *
+ * AND:
+ * @flag: 6
+ * @ptr: mem
+ * @val1: mask
+ * Atomically *mem &= mask and return the old value of *mem.
+ *
+ * OR:
+ * @flag: 7
+ * @ptr: mem
+ * @val1: mask
+ * Atomically *mem |= mask and return the old value of *mem.
+ *
+ * UMAX: unsigned
+ * @flag: 8
+ * @ptr: mem
+ * @val1: max
+ * If *mem < val, set *mem = max. Returns old value of *mem.
+ *
+ * UMIN: unsigned
+ * @flag: 9
+ * @ptr: mem
+ * @val1: min
+ * If *mem > val, set *mem = min. Returns old value of *mem.
*
*/
-
ENTRY(sys_or1k_atomic)
- /* FIXME: This ignores r3 and always does an XCHG */
+ /* TODO: validate mem ptr(s) */
+ /* TODO: check bounds of argument */
+ l.movhi r19,hi(atomic_table)
+ l.ori r19,r19,lo(atomic_table)
+ l.slli r3,r3,3
+ l.add r19,r19,r3
+ l.jr r19
+ l.nop
+atomic_table:
+ l.jr r9
+ l.nop
+ l.j _atomic_swap
+ l.nop
+ l.j _atomic_cmpxchg
+ l.nop
+ l.j _atomic_xchg
+ l.nop
+ l.j _atomic_add
+ l.nop
+ l.j _atomic_decpos
+ l.nop
+ l.j _atomic_and
+ l.nop
+ l.j _atomic_or
+ l.nop
+ l.j _atomic_max
+ l.nop
+ l.j _atomic_min
+ l.nop
+
+_atomic_swap:
DISABLE_INTERRUPTS(r17,r19)
l.lwz r29,0(r4)
l.lwz r27,0(r5)
@@ -1129,4 +1210,90 @@ ENTRY(sys_or1k_atomic)
l.jr r9
l.or r11,r0,r0
+_atomic_cmpxchg:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.sfeq r29,r5
+ l.bnf _atomic_cmpxchg_done
+ l.nop
+
+ l.sw 0(r4),r6
+_atomic_cmpxchg_done:
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_xchg:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.sw 0(r4),r5
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_add:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.add r27,r29,r5
+ l.sw 0(r4),r27
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_decpos:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.sfgtsi r29,0
+ l.bnf _atomic_decpos_done
+ l.addi r27,r29,-1
+
+ l.sw 0(r4),r27
+_atomic_decpos_done:
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_and:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.and r27,r29,r5
+ l.sw 0(r4),r27
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_or:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.or r27,r29,r5
+ l.sw 0(r4),r27
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_max:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.sfltu r29,r5
+ l.bnf _atomic_max_done
+ l.nop
+
+ l.sw 0(r4),r5
+_atomic_max_done:
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
+
+_atomic_min:
+ DISABLE_INTERRUPTS(r17,r19)
+ l.lwz r29,0(r4)
+ l.sfgtu r29,r5
+ l.bnf _atomic_min_done
+ l.nop
+
+ l.sw 0(r4),r5
+_atomic_min_done:
+ ENABLE_INTERRUPTS(r17)
+ l.jr r9
+ l.or r11,r29,r0
/* ============================================================[ EOF ]===
*/
--
1.7.10.4
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openrisc.net/pipermail/linux/attachments/20140125/703b3339/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-openrisc-Add-new-atomic-operations-to-or1k_atomic.patch
Type: application/octet-stream
Size: 5563 bytes
Desc: not available
URL: <http://lists.openrisc.net/pipermail/linux/attachments/20140125/703b3339/attachment-0001.obj>