* [PATCH v3 0/3] Introduce atomic_dec_and_lock_irqsave + refcount_dec_and_lock_irqsave()
@ 2018-06-12 16:16 Sebastian Andrzej Siewior
2018-06-12 16:16 ` [PATCH 1/3] alpha: remove custom dec_and_lock() implementation Sebastian Andrzej Siewior
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-06-12 16:16 UTC (permalink / raw)
To: linux-kernel; +Cc: tglx, Ingo Molnar, Peter Zijlstra
This patchset introduces atomic_dec_and_lock_irqsave() and
refcount_dec_and_lock_irqsave(). The individual user are not part of
this series.
The custom Alpha implementation of atomic_dec_and_lock() is removed
because the decrement operation is not a RELEASE operation (pointed out
by Peter Zijlstra).
Sebastian
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/3] alpha: remove custom dec_and_lock() implementation
2018-06-12 16:16 [PATCH v3 0/3] Introduce atomic_dec_and_lock_irqsave + refcount_dec_and_lock_irqsave() Sebastian Andrzej Siewior
@ 2018-06-12 16:16 ` Sebastian Andrzej Siewior
2018-06-12 16:16 ` [PATCH 2/3] spinlock: atomic_dec_and_lock: Add an irqsave variant Sebastian Andrzej Siewior
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-06-12 16:16 UTC (permalink / raw)
To: linux-kernel
Cc: tglx, Ingo Molnar, Peter Zijlstra, Sebastian Andrzej Siewior,
Richard Henderson, Ivan Kokshaysky, Matt Turner, linux-alpha
Alpha provides a custom implementation of dec_and_lock(). The functions
is split into two parts:
- atomic_add_unless() + return 0 (fast path in assembly)
- remaining part including locking (slow path in C)
Comparing the result of the alpha implementation with the generic
implementation compiled by gcc it looks like the fast path is optimized
by avoiding a stack frame (and reloading the GP), register store and all
this. This is only done in the slowpath.
After marking the slowpath (atomic_dec_and_lock_1()) as "noinline" and
doing the slowpath in C (the atomic_add_unless(atomic, -1, 1) part) I
noticed differences in the resulting assembly:
- the GP is still reloaded
- atomic_add_unless() adds more memory barriers compared to the custom
assembly
- the custom assembly here does "load, sub, beq" while
atomic_add_unless() does "load, cmpeq, add, bne". This is okay because
it compares against zero after subtraction while the generic code
compares against 1 before.
I'm not sure if avoiding the stack frame (and GP reloading) brings a lot
in terms of performance. Regarding the different barriers, Peter
Zijlstra says:
|refcount decrement needs to be a RELEASE operation, such that all the
|load/stores to the object happen before we decrement the refcount.
|
|Otherwise things like:
|
| obj->foo = 5;
| refcnt_dec(&obj->ref);
|
|can be re-ordered, which then allows fun scenarios like:
|
| CPU0 CPU1
|
| refcnt_dec(&obj->ref);
| if (dec_and_test(&obj->ref))
| free(obj);
| obj->foo = 5; // oops UaF
|
|
|This means (for alpha) that there should be a memory barrier _before_
|the decrement, however the dec_and_lock asm thing only has one _after_,
|which, per the above, is too late.
|
|The generic version using add_unless will result in memory barrier
|before and after (because that is the rule for atomic ops with a return
|value) which is strictly too many barriers for the refcount story, but
|who knows what other ordering requirements code has.
I suggest to remove the custom alpha implementation of dec_and_lock()
and if it is an issue (performance wise) we could still inline the fast
path of dec_and_lock().
Link: https://lkml.kernel.org/r/20180606115918.GG12198@hirez.programming.kicks-ass.net
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Matt Turner <mattst88@gmail.com>
Cc: linux-alpha@vger.kernel.org
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
arch/alpha/Kconfig | 5 ----
arch/alpha/lib/Makefile | 2 --
arch/alpha/lib/dec_and_lock.c | 44 -----------------------------------
lib/Makefile | 6 +----
4 files changed, 1 insertion(+), 56 deletions(-)
delete mode 100644 arch/alpha/lib/dec_and_lock.c
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index f19dc31288c8..786649e0fdf9 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -565,11 +565,6 @@ config SMP
If you don't know what to do here, say N.
-config HAVE_DEC_LOCK
- bool
- depends on SMP
- default y
-
config NR_CPUS
int "Maximum number of CPUs (2-32)"
range 2 32
diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile
index 04f9729de57c..854d5e79979e 100644
--- a/arch/alpha/lib/Makefile
+++ b/arch/alpha/lib/Makefile
@@ -35,8 +35,6 @@ lib-y = __divqu.o __remqu.o __divlu.o __remlu.o \
callback_srm.o srm_puts.o srm_printk.o \
fls.o
-lib-$(CONFIG_SMP) += dec_and_lock.o
-
# The division routines are built from single source, with different defines.
AFLAGS___divqu.o = -DDIV
AFLAGS___remqu.o = -DREM
diff --git a/arch/alpha/lib/dec_and_lock.c b/arch/alpha/lib/dec_and_lock.c
deleted file mode 100644
index a117707f57fe..000000000000
--- a/arch/alpha/lib/dec_and_lock.c
+++ /dev/null
@@ -1,44 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * arch/alpha/lib/dec_and_lock.c
- *
- * ll/sc version of atomic_dec_and_lock()
- *
- */
-
-#include <linux/spinlock.h>
-#include <linux/atomic.h>
-#include <linux/export.h>
-
- asm (".text \n\
- .global _atomic_dec_and_lock \n\
- .ent _atomic_dec_and_lock \n\
- .align 4 \n\
-_atomic_dec_and_lock: \n\
- .prologue 0 \n\
-1: ldl_l $1, 0($16) \n\
- subl $1, 1, $1 \n\
- beq $1, 2f \n\
- stl_c $1, 0($16) \n\
- beq $1, 4f \n\
- mb \n\
- clr $0 \n\
- ret \n\
-2: br $29, 3f \n\
-3: ldgp $29, 0($29) \n\
- br $atomic_dec_and_lock_1..ng \n\
- .subsection 2 \n\
-4: br 1b \n\
- .previous \n\
- .end _atomic_dec_and_lock");
-
-static int __used atomic_dec_and_lock_1(atomic_t *atomic, spinlock_t *lock)
-{
- /* Slow path */
- spin_lock(lock);
- if (atomic_dec_and_test(atomic))
- return 1;
- spin_unlock(lock);
- return 0;
-}
-EXPORT_SYMBOL(_atomic_dec_and_lock);
diff --git a/lib/Makefile b/lib/Makefile
index ce20696d5a92..d0c1531c43f4 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -23,7 +23,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
sha1.o chacha20.o irq_regs.o argv_split.o \
flex_proportions.o ratelimit.o show_mem.o \
is_single_threaded.o plist.o decompress.o kobject_uevent.o \
- earlycpio.o seq_buf.o siphash.o \
+ earlycpio.o seq_buf.o siphash.o dec_and_lock.o \
nmi_backtrace.o nodemask.o win_minmax.o
lib-$(CONFIG_PRINTK) += dump_stack.o
@@ -96,10 +96,6 @@ obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
obj-$(CONFIG_DEBUG_LIST) += list_debug.o
obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
-ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
- lib-y += dec_and_lock.o
-endif
-
obj-$(CONFIG_BITREVERSE) += bitrev.o
obj-$(CONFIG_RATIONAL) += rational.o
obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o
--
2.17.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/3] spinlock: atomic_dec_and_lock: Add an irqsave variant
2018-06-12 16:16 [PATCH v3 0/3] Introduce atomic_dec_and_lock_irqsave + refcount_dec_and_lock_irqsave() Sebastian Andrzej Siewior
2018-06-12 16:16 ` [PATCH 1/3] alpha: remove custom dec_and_lock() implementation Sebastian Andrzej Siewior
@ 2018-06-12 16:16 ` Sebastian Andrzej Siewior
2018-06-12 16:16 ` [PATCH 3/3] locking/refcount: implement refcount_dec_and_lock_irqsave() Sebastian Andrzej Siewior
2018-06-12 20:14 ` [PATCH v3 0/3] Introduce atomic_dec_and_lock_irqsave + refcount_dec_and_lock_irqsave() Peter Zijlstra
3 siblings, 0 replies; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-06-12 16:16 UTC (permalink / raw)
To: linux-kernel
Cc: tglx, Ingo Molnar, Peter Zijlstra, Anna-Maria Gleixner,
Sebastian Andrzej Siewior
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
There are in-tree users of atomic_dec_and_lock() which must acquire the
spin lock with interrupts disabled. To workaround the lack of an irqsave
variant of atomic_dec_and_lock() they use local_irq_save() at the call
site. This causes extra code and creates in some places unneeded long
interrupt disabled times. These places need also extra treatment for
PREEMPT_RT due to the disconnect of the irq disabling and the lock
function.
Implement the missing irqsave variant of the function.
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
include/linux/spinlock.h | 5 +++++
lib/dec_and_lock.c | 16 ++++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h
index 4894d322d258..803536c992f5 100644
--- a/include/linux/spinlock.h
+++ b/include/linux/spinlock.h
@@ -409,6 +409,11 @@ extern int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock);
#define atomic_dec_and_lock(atomic, lock) \
__cond_lock(lock, _atomic_dec_and_lock(atomic, lock))
+extern int _atomic_dec_and_lock_irqsave(atomic_t *atomic, spinlock_t *lock,
+ unsigned long *flags);
+#define atomic_dec_and_lock_irqsave(atomic, lock, flags) \
+ __cond_lock(lock, _atomic_dec_and_lock_irqsave(atomic, lock, &(flags)))
+
int alloc_bucket_spinlocks(spinlock_t **locks, unsigned int *lock_mask,
size_t max_size, unsigned int cpu_mult,
gfp_t gfp);
diff --git a/lib/dec_and_lock.c b/lib/dec_and_lock.c
index 347fa7ac2e8a..9555b68bb774 100644
--- a/lib/dec_and_lock.c
+++ b/lib/dec_and_lock.c
@@ -33,3 +33,19 @@ int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock)
}
EXPORT_SYMBOL(_atomic_dec_and_lock);
+
+int _atomic_dec_and_lock_irqsave(atomic_t *atomic, spinlock_t *lock,
+ unsigned long *flags)
+{
+ /* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */
+ if (atomic_add_unless(atomic, -1, 1))
+ return 0;
+
+ /* Otherwise do it the slow way */
+ spin_lock_irqsave(lock, *flags);
+ if (atomic_dec_and_test(atomic))
+ return 1;
+ spin_unlock_irqrestore(lock, *flags);
+ return 0;
+}
+EXPORT_SYMBOL(_atomic_dec_and_lock_irqsave);
--
2.17.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 3/3] locking/refcount: implement refcount_dec_and_lock_irqsave()
2018-06-12 16:16 [PATCH v3 0/3] Introduce atomic_dec_and_lock_irqsave + refcount_dec_and_lock_irqsave() Sebastian Andrzej Siewior
2018-06-12 16:16 ` [PATCH 1/3] alpha: remove custom dec_and_lock() implementation Sebastian Andrzej Siewior
2018-06-12 16:16 ` [PATCH 2/3] spinlock: atomic_dec_and_lock: Add an irqsave variant Sebastian Andrzej Siewior
@ 2018-06-12 16:16 ` Sebastian Andrzej Siewior
2018-06-12 20:14 ` [PATCH v3 0/3] Introduce atomic_dec_and_lock_irqsave + refcount_dec_and_lock_irqsave() Peter Zijlstra
3 siblings, 0 replies; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-06-12 16:16 UTC (permalink / raw)
To: linux-kernel
Cc: tglx, Ingo Molnar, Peter Zijlstra, Anna-Maria Gleixner,
Sebastian Andrzej Siewior
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
There are in-tree users of refcount_dec_and_lock() which must acquire the
spin lock with interrupts disabled. To workaround the lack of an irqsave
variant of refcount_dec_and_lock() they use local_irq_save() at the call
site. This causes extra code and creates in some places unneeded long
interrupt disabled times. These places need also extra treatment for
PREEMPT_RT due to the disconnect of the irq disabling and the lock
function.
Implement the missing irqsave variant of the function.
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
[bigeasy: s@atomic_dec_and_lock@refcount_dec_and_lock@g]
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
include/linux/refcount.h | 4 +++-
lib/refcount.c | 28 ++++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/include/linux/refcount.h b/include/linux/refcount.h
index 4193c41e383a..a685da2c4522 100644
--- a/include/linux/refcount.h
+++ b/include/linux/refcount.h
@@ -98,5 +98,7 @@ extern __must_check bool refcount_dec_if_one(refcount_t *r);
extern __must_check bool refcount_dec_not_one(refcount_t *r);
extern __must_check bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock);
extern __must_check bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock);
-
+extern __must_check bool refcount_dec_and_lock_irqsave(refcount_t *r,
+ spinlock_t *lock,
+ unsigned long *flags);
#endif /* _LINUX_REFCOUNT_H */
diff --git a/lib/refcount.c b/lib/refcount.c
index 0eb48353abe3..d3b81cefce91 100644
--- a/lib/refcount.c
+++ b/lib/refcount.c
@@ -350,3 +350,31 @@ bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock)
}
EXPORT_SYMBOL(refcount_dec_and_lock);
+/**
+ * refcount_dec_and_lock_irqsave - return holding spinlock with disabled
+ * interrupts if able to decrement refcount to 0
+ * @r: the refcount
+ * @lock: the spinlock to be locked
+ * @flags: saved IRQ-flags if the is acquired
+ *
+ * Same as refcount_dec_and_lock() above except that the spinlock is acquired
+ * with disabled interupts.
+ *
+ * Return: true and hold spinlock if able to decrement refcount to 0, false
+ * otherwise
+ */
+bool refcount_dec_and_lock_irqsave(refcount_t *r, spinlock_t *lock,
+ unsigned long *flags)
+{
+ if (refcount_dec_not_one(r))
+ return false;
+
+ spin_lock_irqsave(lock, *flags);
+ if (!refcount_dec_and_test(r)) {
+ spin_unlock_irqrestore(lock, *flags);
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(refcount_dec_and_lock_irqsave);
--
2.17.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v3 0/3] Introduce atomic_dec_and_lock_irqsave + refcount_dec_and_lock_irqsave()
2018-06-12 16:16 [PATCH v3 0/3] Introduce atomic_dec_and_lock_irqsave + refcount_dec_and_lock_irqsave() Sebastian Andrzej Siewior
` (2 preceding siblings ...)
2018-06-12 16:16 ` [PATCH 3/3] locking/refcount: implement refcount_dec_and_lock_irqsave() Sebastian Andrzej Siewior
@ 2018-06-12 20:14 ` Peter Zijlstra
3 siblings, 0 replies; 5+ messages in thread
From: Peter Zijlstra @ 2018-06-12 20:14 UTC (permalink / raw)
To: Sebastian Andrzej Siewior; +Cc: linux-kernel, tglx, Ingo Molnar
On Tue, Jun 12, 2018 at 06:16:18PM +0200, Sebastian Andrzej Siewior wrote:
> This patchset introduces atomic_dec_and_lock_irqsave() and
> refcount_dec_and_lock_irqsave(). The individual user are not part of
> this series.
> The custom Alpha implementation of atomic_dec_and_lock() is removed
> because the decrement operation is not a RELEASE operation (pointed out
> by Peter Zijlstra).
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2018-06-12 20:14 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-12 16:16 [PATCH v3 0/3] Introduce atomic_dec_and_lock_irqsave + refcount_dec_and_lock_irqsave() Sebastian Andrzej Siewior
2018-06-12 16:16 ` [PATCH 1/3] alpha: remove custom dec_and_lock() implementation Sebastian Andrzej Siewior
2018-06-12 16:16 ` [PATCH 2/3] spinlock: atomic_dec_and_lock: Add an irqsave variant Sebastian Andrzej Siewior
2018-06-12 16:16 ` [PATCH 3/3] locking/refcount: implement refcount_dec_and_lock_irqsave() Sebastian Andrzej Siewior
2018-06-12 20:14 ` [PATCH v3 0/3] Introduce atomic_dec_and_lock_irqsave + refcount_dec_and_lock_irqsave() Peter Zijlstra
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®