Skip to contentSkip to conversation

kref: inline and barriers

PATCH In review Peter Zijlstra 8 participants 41 messages Dec 10, 2011 – Dec 13, 2011 Library code

lore
Share & tools
lore
#0 lore raw

cover kref: inline and barriers

Quoted 1 line

From time to time people on #kernelnewbies ask what the difference is between

kref and using atomic_t yourself. And I keep having to tell them that kref is the slow and weird option.

These patches try and cure some of that, it inlines all functions and removes the superfluous memory barriers that are there for no good reason at all.

#1 lore raw

PATCH 1/3 kref: Inline all functions

These are tiny functions, there's no point in having them out-of-line.

---
 include/linux/kref.h |   79 ++++++++++++++++++++++++++++++++++++++---
 lib/Makefile         |    2 -
 lib/kref.c           |   97 ---------------------------------------------------
 3 files changed, 75 insertions(+), 103 deletions(-)
include/linux/kref.h +74 -5
Index: linux-2.6/include/linux/kref.h
===================================================================
@@ -21,10 +21,79 @@ struct kref {
 	atomic_t refcount;
 };
 
-void kref_init(struct kref *kref);
-void kref_get(struct kref *kref);
-int kref_put(struct kref *kref, void (*release) (struct kref *kref));
-int kref_sub(struct kref *kref, unsigned int count,
-	     void (*release) (struct kref *kref));
+/**
+ * kref_init - initialize object.
+ * @kref: object in question.
+ */
+static inline void kref_init(struct kref *kref)
+{
+	atomic_set(&kref->refcount, 1);
+	smp_mb();
+}
 
+/**
+ * kref_get - increment refcount for object.
+ * @kref: object.
+ */
+static inline void kref_get(struct kref *kref)
+{
+	WARN_ON(!atomic_read(&kref->refcount));
+	atomic_inc(&kref->refcount);
+	smp_mb__after_atomic_inc();
+}
+
+/**
+ * kref_put - decrement refcount for object.
+ * @kref: object.
+ * @release: pointer to the function that will clean up the object when the
+ *	     last reference to the object is released.
+ *	     This pointer is required, and it is not acceptable to pass kfree
+ *	     in as this function.
+ *
+ * Decrement the refcount, and if 0, call release().
+ * Return 1 if the object was removed, otherwise return 0.  Beware, if this
+ * function returns 0, you still can not count on the kref from remaining in
+ * memory.  Only use the return value if you want to see if the kref is now
+ * gone, not present.
+ */
+static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
+{
+	WARN_ON(release == NULL);
+	WARN_ON(release == (void (*)(struct kref *))kfree);
+
+	if (atomic_dec_and_test(&kref->refcount)) {
+		release(kref);
+		return 1;
+	}
+	return 0;
+}
+
+
+/**
+ * kref_sub - subtract a number of refcounts for object.
+ * @kref: object.
+ * @count: Number of recounts to subtract.
+ * @release: pointer to the function that will clean up the object when the
+ *	     last reference to the object is released.
+ *	     This pointer is required, and it is not acceptable to pass kfree
+ *	     in as this function.
+ *
+ * Subtract @count from the refcount, and if 0, call release().
+ * Return 1 if the object was removed, otherwise return 0.  Beware, if this
+ * function returns 0, you still can not count on the kref from remaining in
+ * memory.  Only use the return value if you want to see if the kref is now
+ * gone, not present.
+ */
+static inline int kref_sub(struct kref *kref, unsigned int count,
+	     void (*release)(struct kref *kref))
+{
+	WARN_ON(release == NULL);
+	WARN_ON(release == (void (*)(struct kref *))kfree);
+
+	if (atomic_sub_and_test((int) count, &kref->refcount)) {
+		release(kref);
+		return 1;
+	}
+	return 0;
+}
 #endif /* _KREF_H_ */
lib/kref.c deleted +0 -97

97 lines not on this page · raw message

lib/Makefile +1 -1
Index: linux-2.6/lib/Makefile
===================================================================
@@ -17,7 +17,7 @@ lib-y := ctype.o string.o vsprintf.o cmd
 lib-$(CONFIG_MMU) += ioremap.o
 lib-$(CONFIG_SMP) += cpumask.o
 
-lib-y	+= kobject.o kref.o klist.o
+lib-y	+= kobject.o klist.o
 
 obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
 	 bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
#2 lore raw

Re: [PATCH 1/3] kref: Inline all functions

Hi,

Quoted 1 line · Peter Zijlstra

These are tiny functions, there's no point in having them out-of-line.

Looks like no much text size increase in built vmlinux for ARM.

[[email protected]]$size vmlinux
text data bss dec hex filename
6030247 2083292 7912356 16025895 f48927 vmlinux

[[email protected]]$size vmlinux-before-kref-inline
text data bss dec hex filename
6023264 2083300 7912356 16018920 f46de8 vmlinux-before-kref-inline

Quoted 53 lines

---
 include/linux/kref.h |   79 ++++++++++++++++++++++++++++++++++++++--- lib/Makefile         |    2 -
 lib/kref.c           |   97 ---------------------------------------------------

 3 files changed, 75 insertions(+), 103 deletions(-)

Index: linux-2.6/include/linux/kref.h
===================================================================

include/linux/kref.h +28 -5
@@ -21,10 +21,79 @@ struct kref {
        atomic_t refcount;
  };
 
-void kref_init(struct kref *kref);
-void kref_get(struct kref *kref);
-int kref_put(struct kref *kref, void (*release) (struct kref *kref));
-int kref_sub(struct kref *kref, unsigned int count,
-            void (*release) (struct kref *kref));
+/**
+ * kref_init - initialize object.
+ * @kref: object in question.
+ */
+static inline void kref_init(struct kref *kref)
+{
+       atomic_set(&kref->refcount, 1);
+       smp_mb();
+}
 
+/**
+ * kref_get - increment refcount for object.
+ * @kref: object.
+ */
+static inline void kref_get(struct kref *kref)
+{
+       WARN_ON(!atomic_read(&kref->refcount));
+       atomic_inc(&kref->refcount);
+       smp_mb__after_atomic_inc();
+}
+
+/**
+ * kref_put - decrement refcount for object.
+ * @kref: object.
+ * @release: pointer to the function that will clean up the object when the
+ *          last reference to the object is released.
+ *          This pointer is required, and it is not acceptable to pass kfree
+ *          in as this function.
+ *

include/linux/kref.h struct kref { · line 62

Quoted 10 lines
include/linux/kref.h +10 -0
+ * Decrement the refcount, and if 0, call release().
+ * Return 1 if the object was removed, otherwise return 0.  Beware, if this
+ * function returns 0, you still can not count on the kref from remaining in
+ * memory.  Only use the return value if you want to see if the kref is now
+ * gone, not present.
+ */
+static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
+{
+       WARN_ON(release == NULL);
+       WARN_ON(release == (void (*)(struct kref *))kfree);

<linux/slab.h> is needed to avoid compiling failure.

Quoted 154 lines
include/linux/kref.h +36 -0
+
+       if (atomic_dec_and_test(&kref->refcount)) {
+               release(kref);
+               return 1;
+       }
+       return 0;
+}
+
+
+/**
+ * kref_sub - subtract a number of refcounts for object.
+ * @kref: object.
+ * @count: Number of recounts to subtract.
+ * @release: pointer to the function that will clean up the object when the
+ *          last reference to the object is released.
+ *          This pointer is required, and it is not acceptable to pass kfree
+ *          in as this function.
+ *
+ * Subtract @count from the refcount, and if 0, call release().
+ * Return 1 if the object was removed, otherwise return 0.  Beware, if this
+ * function returns 0, you still can not count on the kref from remaining in
+ * memory.  Only use the return value if you want to see if the kref is now
+ * gone, not present.
+ */
+static inline int kref_sub(struct kref *kref, unsigned int count,
+            void (*release)(struct kref *kref))
+{
+       WARN_ON(release == NULL);
+       WARN_ON(release == (void (*)(struct kref *))kfree);
+
+       if (atomic_sub_and_test((int) count, &kref->refcount)) {
+               release(kref);
+               return 1;
+       }
+       return 0;
+}
  #endif /* _KREF_H_ */

Index: linux-2.6/lib/kref.c
===================================================================

lib/kref.c deleted +0 -97
@@ -1,97 +0,0 @@
-/*
- * kref.c - library routines for handling generic reference counted objects
- *
- * Copyright (C) 2004 Greg Kroah-Hartman <[email protected]>
- * Copyright (C) 2004 IBM Corp.
- *
- * based on lib/kobject.c which was:
- * Copyright (C) 2002-2003 Patrick Mochel <[email protected]>
- *
- * This file is released under the GPLv2.
- *
- */
-
-#include <linux/kref.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-
-/**
- * kref_init - initialize object.
- * @kref: object in question.
- */
-void kref_init(struct kref *kref)
-{
-       atomic_set(&kref->refcount, 1);
-       smp_mb();
-}
-
-/**
- * kref_get - increment refcount for object.
- * @kref: object.
- */
-void kref_get(struct kref *kref)
-{
-       WARN_ON(!atomic_read(&kref->refcount));
-       atomic_inc(&kref->refcount);
-       smp_mb__after_atomic_inc();
-}
-
-/**
- * kref_put - decrement refcount for object.
- * @kref: object.
- * @release: pointer to the function that will clean up the object when the
- *          last reference to the object is released.
- *          This pointer is required, and it is not acceptable to pass kfree
- *          in as this function.
- *
- * Decrement the refcount, and if 0, call release().
- * Return 1 if the object was removed, otherwise return 0.  Beware, if this
- * function returns 0, you still can not count on the kref from remaining in
- * memory.  Only use the return value if you want to see if the kref is now
- * gone, not present.
- */
-int kref_put(struct kref *kref, void (*release)(struct kref *kref))
-{
-       WARN_ON(release == NULL);
-       WARN_ON(release == (void (*)(struct kref *))kfree);
-
-       if (atomic_dec_and_test(&kref->refcount)) {
-               release(kref);
-               return 1;
-       }
-       return 0;
-}
-
-
-/**
- * kref_sub - subtract a number of refcounts for object.
- * @kref: object.
- * @count: Number of recounts to subtract.
- * @release: pointer to the function that will clean up the object when the
- *          last reference to the object is released.
- *          This pointer is required, and it is not acceptable to pass kfree
- *          in as this function.
- *
- * Subtract @count from the refcount, and if 0, call release().
- * Return 1 if the object was removed, otherwise return 0.  Beware, if this
- * function returns 0, you still can not count on the kref from remaining in
- * memory.  Only use the return value if you want to see if the kref is now
- * gone, not present.
- */
-int kref_sub(struct kref *kref, unsigned int count,
-            void (*release)(struct kref *kref))
-{
-       WARN_ON(release == NULL);
-       WARN_ON(release == (void (*)(struct kref *))kfree);
-
-       if (atomic_sub_and_test((int) count, &kref->refcount)) {
-               release(kref);
-               return 1;
-       }
-       return 0;
-}
-
-EXPORT_SYMBOL(kref_init);
-EXPORT_SYMBOL(kref_get);
-EXPORT_SYMBOL(kref_put);
-EXPORT_SYMBOL(kref_sub);

Index: linux-2.6/lib/Makefile
===================================================================

lib/Makefile +0 -0
@@ -17,7 +17,7 @@ lib-y := ctype.o string.o vsprintf.o cmd

 lib-$(CONFIG_MMU) += ioremap.o
 lib-$(CONFIG_SMP) += cpumask.o

lib/Makefile +1 -1
-lib-y  += kobject.o kref.o klist.o
+lib-y  += kobject.o klist.o

 obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
        bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \

Quoted 5 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

thanks,

Signature · 1 line
Ming Lei
#3 lore raw

Re: [PATCH 1/3] kref: Inline all functions

Quoted 9 lines · Ming Lei

Looks like no much text size increase in built vmlinux for ARM.

[[email protected]]$size vmlinux
text data bss dec hex filename
6030247 2083292 7912356 16025895 f48927 vmlinux

[[email protected]]$size vmlinux-before-kref-inline
text data bss dec hex filename
6023264 2083300 7912356 16018920 f46de8 vmlinux-before-kref-inline

Yeah, and I guess you can reduce it even further if you put the WARN_ON() in some CONFIG_DEBUG_BLAH section, like the refcount stuff from Alexey was doing.

#4 lore raw

Re: [PATCH 1/3] kref: Inline all functions

In reply to Peter Zijlstra
Quoted 1 line · Peter Zijlstra

These are tiny functions, there's no point in having them out-of-line.

Nice, but this breaks the build:

CC init/main.o
In file included from include/linux/kobject.h:24:0,

                 from include/linux/module.h:16,
                 from init/main.c:13:

include/linux/kref.h: In function ‘kref_put’:
include/linux/kref.h:62:2: error: ‘kfree’ undeclared (first use in this function) include/linux/kref.h:62:2: note: each undeclared identifier is reported only once for each function it appears in include/linux/kref.h: In function ‘kref_sub’:
include/linux/kref.h:91:2: error: ‘kfree’ undeclared (first use in this function)
make[1]: *** [init/main.o] Error 1
make: *** [init] Error 2

Sorry, I can't apply this.

This was on an x86-64 build, my "stock" desktop system, nothing "odd" in the config at all...

greg k-h

#5 lore raw

Re: [PATCH 1/3] kref: Inline all functions

Quoted 1 line · Greg KH

Nice, but this breaks the build:

Sorry about that, this one includes the missing include and just booted on my test-box.

---
Subject: kref: Inline all functions
From: Peter Zijlstra <[email protected]>
Date: Sat Dec 10 11:29:57 CET 2011

These are tiny functions, there's no point in having them out-of-line.

---
 include/linux/kref.h |   80 +++++++++++++++++++++++++++++++++++++++---
 lib/Makefile         |    2 -
 lib/kref.c           |   97 ---------------------------------------------------
 3 files changed, 76 insertions(+), 103 deletions(-)
include/linux/kref.h +75 -5
Index: linux-2.6/include/linux/kref.h
===================================================================
@@ -16,15 +16,85 @@
 #define _KREF_H_
 
 #include <linux/types.h>
+#include <linux/slab.h>
 
 struct kref {
 	atomic_t refcount;
 };
 
-void kref_init(struct kref *kref);
-void kref_get(struct kref *kref);
-int kref_put(struct kref *kref, void (*release) (struct kref *kref));
-int kref_sub(struct kref *kref, unsigned int count,
-	     void (*release) (struct kref *kref));
+/**
+ * kref_init - initialize object.
+ * @kref: object in question.
+ */
+static inline void kref_init(struct kref *kref)
+{
+	atomic_set(&kref->refcount, 1);
+	smp_mb();
+}
 
+/**
+ * kref_get - increment refcount for object.
+ * @kref: object.
+ */
+static inline void kref_get(struct kref *kref)
+{
+	WARN_ON(!atomic_read(&kref->refcount));
+	atomic_inc(&kref->refcount);
+	smp_mb__after_atomic_inc();
+}
+
+/**
+ * kref_put - decrement refcount for object.
+ * @kref: object.
+ * @release: pointer to the function that will clean up the object when the
+ *	     last reference to the object is released.
+ *	     This pointer is required, and it is not acceptable to pass kfree
+ *	     in as this function.
+ *
+ * Decrement the refcount, and if 0, call release().
+ * Return 1 if the object was removed, otherwise return 0.  Beware, if this
+ * function returns 0, you still can not count on the kref from remaining in
+ * memory.  Only use the return value if you want to see if the kref is now
+ * gone, not present.
+ */
+static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
+{
+	WARN_ON(release == NULL);
+	WARN_ON(release == (void (*)(struct kref *))kfree);
+
+	if (atomic_dec_and_test(&kref->refcount)) {
+		release(kref);
+		return 1;
+	}
+	return 0;
+}
+
+
+/**
+ * kref_sub - subtract a number of refcounts for object.
+ * @kref: object.
+ * @count: Number of recounts to subtract.
+ * @release: pointer to the function that will clean up the object when the
+ *	     last reference to the object is released.
+ *	     This pointer is required, and it is not acceptable to pass kfree
+ *	     in as this function.
+ *
+ * Subtract @count from the refcount, and if 0, call release().
+ * Return 1 if the object was removed, otherwise return 0.  Beware, if this
+ * function returns 0, you still can not count on the kref from remaining in
+ * memory.  Only use the return value if you want to see if the kref is now
+ * gone, not present.
+ */
+static inline int kref_sub(struct kref *kref, unsigned int count,
+	     void (*release)(struct kref *kref))
+{
+	WARN_ON(release == NULL);
+	WARN_ON(release == (void (*)(struct kref *))kfree);
+
+	if (atomic_sub_and_test((int) count, &kref->refcount)) {
+		release(kref);
+		return 1;
+	}
+	return 0;
+}
 #endif /* _KREF_H_ */
lib/kref.c deleted +0 -97

97 lines not on this page · raw message

lib/Makefile +1 -1
Index: linux-2.6/lib/Makefile
===================================================================
@@ -17,7 +17,7 @@ lib-y := ctype.o string.o vsprintf.o cmd
 lib-$(CONFIG_MMU) += ioremap.o
 lib-$(CONFIG_SMP) += cpumask.o
 
-lib-y	+= kobject.o kref.o klist.o
+lib-y	+= kobject.o klist.o
 
 obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
 	 bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
#6 lore raw

Re: [PATCH 1/3] kref: Inline all functions

Quoted 5 lines · Peter Zijlstra
Quoted 1 line · Greg KH

Nice, but this breaks the build:

Sorry about that, this one includes the missing include and just booted on my test-box.

Thanks, that worked.

Quoted 13 lines

---
Subject: kref: Inline all functions
From: Peter Zijlstra <[email protected]>
Date: Sat Dec 10 11:29:57 CET 2011

These are tiny functions, there's no point in having them out-of-line.

Quoted 7 lines
---
 include/linux/kref.h |   80 +++++++++++++++++++++++++++++++++++++++---
 lib/Makefile         |    2 -
 lib/kref.c           |   97 ---------------------------------------------------
 3 files changed, 76 insertions(+), 103 deletions(-)

Index: linux-2.6/include/linux/kref.h
===================================================================

What's this stuff for, we aren't using cvs anymore :)

greg k-h

#7 lore raw

Re: [PATCH 1/3] kref: Inline all functions

Quoted 9 lines · Greg KH
Quoted 7 lines
---
 include/linux/kref.h |   80 +++++++++++++++++++++++++++++++++++++++---
 lib/Makefile         |    2 -
 lib/kref.c           |   97 ---------------------------------------------------
 3 files changed, 76 insertions(+), 103 deletions(-)

Index: linux-2.6/include/linux/kref.h
===================================================================

What's this stuff for, we aren't using cvs anymore :)

Dunno, it looks like quilt generates that for me.

#8 lore raw

Re: [PATCH 1/3] kref: Inline all functions

Quoted 10 lines · Peter Zijlstra
Quoted 8 lines · Greg KH
Quoted 7 lines
---
 include/linux/kref.h |   80 +++++++++++++++++++++++++++++++++++++++---
 lib/Makefile         |    2 -
 lib/kref.c           |   97 ---------------------------------------------------
 3 files changed, 76 insertions(+), 103 deletions(-)

Index: linux-2.6/include/linux/kref.h
===================================================================

Quoted 3 lines
Quoted 1 line

What's this stuff for, we aren't using cvs anymore :)

Dunno, it looks like quilt generates that for me.

You need to put:
QUILT_REFRESH_ARGS="--diffstat --strip-trailing-whitespace --no-timestamps --no-index --sort -p1 -p ab" QUILT_DIFF_ARGS="--no-timestamps --no-index --sort --color=auto -p ab"

into your .quiltrc so the index markings will not show up, as well as using some other "sane" defaults for generating patches.

greg k-h

#9 lore raw

Re: [PATCH 1/3] kref: Inline all functions

Quoted 3 lines · Greg KH

QUILT_REFRESH_ARGS="--diffstat --strip-trailing-whitespace --no-timestamps --no-index --sort -p1 -p ab" QUILT_DIFF_ARGS="--no-timestamps --no-index --sort --color=auto -p ab"

Ah, its the --no-index that does that. I also missed the --sort and -p ab thing, the rest I already had.

Thanks!

#10 lore raw

PATCH 2/3 kref: Implement kref_put in terms of kref_sub

In reply to Peter Zijlstra

Less lines of code is better.

---
 include/linux/kref.h |   28 ++++++++++------------------
 1 file changed, 10 insertions(+), 18 deletions(-)
include/linux/kref.h +10 -18
Index: linux-2.6/include/linux/kref.h
===================================================================
@@ -43,57 +43,49 @@ static inline void kref_get(struct kref
 }
 
 /**
- * kref_put - decrement refcount for object.
+ * kref_sub - subtract a number of refcounts for object.
  * @kref: object.
+ * @count: Number of recounts to subtract.
  * @release: pointer to the function that will clean up the object when the
  *	     last reference to the object is released.
  *	     This pointer is required, and it is not acceptable to pass kfree
  *	     in as this function.
  *
- * Decrement the refcount, and if 0, call release().
+ * Subtract @count from the refcount, and if 0, call release().
  * Return 1 if the object was removed, otherwise return 0.  Beware, if this
  * function returns 0, you still can not count on the kref from remaining in
  * memory.  Only use the return value if you want to see if the kref is now
  * gone, not present.
  */
-static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
+static inline int kref_sub(struct kref *kref, unsigned int count,
+	     void (*release)(struct kref *kref))
 {
 	WARN_ON(release == NULL);
 	WARN_ON(release == (void (*)(struct kref *))kfree);
 
-	if (atomic_dec_and_test(&kref->refcount)) {
+	if (atomic_sub_and_test((int) count, &kref->refcount)) {
 		release(kref);
 		return 1;
 	}
 	return 0;
 }
 
-
 /**
- * kref_sub - subtract a number of refcounts for object.
+ * kref_put - decrement refcount for object.
  * @kref: object.
- * @count: Number of recounts to subtract.
  * @release: pointer to the function that will clean up the object when the
  *	     last reference to the object is released.
  *	     This pointer is required, and it is not acceptable to pass kfree
  *	     in as this function.
  *
- * Subtract @count from the refcount, and if 0, call release().
+ * Decrement the refcount, and if 0, call release().
  * Return 1 if the object was removed, otherwise return 0.  Beware, if this
  * function returns 0, you still can not count on the kref from remaining in
  * memory.  Only use the return value if you want to see if the kref is now
  * gone, not present.
  */
-static inline int kref_sub(struct kref *kref, unsigned int count,
-	     void (*release)(struct kref *kref))
+static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
 {
-	WARN_ON(release == NULL);
-	WARN_ON(release == (void (*)(struct kref *))kfree);
-
-	if (atomic_sub_and_test((int) count, &kref->refcount)) {
-		release(kref);
-		return 1;
-	}
-	return 0;
+	return kref_sub(kref, 1, release);
 }
 #endif /* _KREF_H_ */
#11 lore raw

PATCH 3/3 kref: Remove the memory barriers

In reply to Peter Zijlstra

Commit 1b0b3b9980e ("kref: fix CPU ordering with respect to krefs") wrongly adds memory barriers to kref.

It states:

some atomic operations are only atomic, not ordered. Thus a CPU is allowed to reorder memory references to an object to before the reference is obtained. This fixes it.

While true, it fails to show why this is a problem. I say it is not a problem because if there is a race with kref_put() such that we could end up referencing a free'd object without this memory barrier, we would still have that race with the memory barrier.

The kref_put() in question could complete (and free the object) before the atomic_inc() and we'd still be up shit creek.

The kref_init() case is even worse, if your object is published at this time you're so wrong the memory barrier won't make a difference what so ever. If its not published, the act of publishing should include the needed barriers/locks to make sure all writes prior to the act of publishing are complete such that others will only observe a complete object.

---
 include/linux/kref.h |    2 --
 1 file changed, 2 deletions(-)
include/linux/kref.h +0 -2
Index: linux-2.6/include/linux/kref.h
===================================================================
@@ -28,7 +28,6 @@ struct kref {
 static inline void kref_init(struct kref *kref)
 {
 	atomic_set(&kref->refcount, 1);
-	smp_mb();
 }
 
 /**
@@ -39,7 +38,6 @@ static inline void kref_get(struct kref
 {
 	WARN_ON(!atomic_read(&kref->refcount));
 	atomic_inc(&kref->refcount);
-	smp_mb__after_atomic_inc();
 }
 
 /**
#12 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Hi,

Quoted 10 lines · Peter Zijlstra

Commit 1b0b3b9980e ("kref: fix CPU ordering with respect to krefs") wrongly adds memory barriers to kref.

It states:

 some atomic operations are only atomic, not ordered. Thus a CPU is allowed to reorder memory references to an object to before the reference is obtained. This fixes it.

While true, it fails to show why this is a problem. I say it is not a

IMO, the added two barriers are pairs of the implicit barrier in kref_put, so that we can order between kref_init/kref_get and kref_put.

Quoted 46 lines

problem because if there is a race with kref_put() such that we could end up referencing a free'd object without this memory barrier, we would still have that race with the memory barrier.

The kref_put() in question could complete (and free the object) before the atomic_inc() and we'd still be up shit creek.

The kref_init() case is even worse, if your object is published at this time you're so wrong the memory barrier won't make a difference what so ever. If its not published, the act of publishing should include the needed barriers/locks to make sure all writes prior to the act of publishing are complete such that others will only observe a complete object.

---
 include/linux/kref.h |    2 --

 1 file changed, 2 deletions(-)

Index: linux-2.6/include/linux/kref.h
===================================================================

include/linux/kref.h +0 -3
@@ -28,7 +28,6 @@ struct kref {
  static inline void kref_init(struct kref *kref)
  {
        atomic_set(&kref->refcount, 1);
-       smp_mb();
  }
 
  /**
@@ -39,7 +38,6 @@ static inline void kref_get(struct kref
  {
        WARN_ON(!atomic_read(&kref->refcount));
        atomic_inc(&kref->refcount);
-       smp_mb__after_atomic_inc();
  }
 
  /**
 
 
--
Quoted 4 lines

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

thanks,

Signature · 1 line
Ming Lei
#13 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 4 lines · Ming Lei
Quoted 1 line

While true, it fails to show why this is a problem. I say it is not a

IMO, the added two barriers are pairs of the implicit barrier in kref_put, so that we can order between kref_init/kref_get and kref_put.

Yeah so? If there's a destruction race with kref_put() the barrier won't solve it. Other than that the actual order of get/put is irrelevant.

#14 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 7 lines · Peter Zijlstra
Quoted 4 lines · Ming Lei
Quoted 1 line

While true, it fails to show why this is a problem. I say it is not a

IMO, the added two barriers are pairs of the implicit barrier in kref_put, so that we can order between kref_init/kref_get and kref_put.

Yeah so?

I think so, see below:

CPU0 CPU1

atomic_set(v)
smp_mb()

				smp_mb()
				atomic_dec_and_test(v)

Without the barrier after atomic_set, CPU1 may see a stale value of v first, then decrease it, so may miss a release operation.

The pair of smp_mb can make order between atomic_set and atomic_dec_and_test, can't it?

Quoted 1 line

If there's a destruction race with kref_put() the barrier won't

Sorry, could you say what the destruction race is?

Quoted 1 line

solve it. Other than that the actual order of get/put is irrelevant.

thanks,

Signature · 1 line
Ming Lei
#15 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 9 lines · Ming Lei

CPU0 CPU1

atomic_set(v)
smp_mb()

				smp_mb()
				atomic_dec_and_test(v)

Without the barrier after atomic_set, CPU1 may see a stale value of v first, then decrease it, so may miss a release operation.

Your example is doubly broken. If there's concurrency possible with atomic_set() you've lost.

Lets change it to kref_get() aka atomic_inc():

	CPU0		CPU1

	atomic_inc()
			atomic_dec_and_test()

and

atomic_dec_and_test()
atomic_inc()

For if the first is possible, then so is the second.

This illustrates that no matter how many barriers you put in, you're still up shit creek without no paddle because the kref_put() can come in before you do the kref_get(), making the kref_get() the invalid operation.

Quoted 2 lines

The pair of smp_mb can make order between atomic_set and atomic_dec_and_test, can't it?

No. Because there's nothing stopping the dec from happening before the set/inc.

Quoted 3 lines
Quoted 1 line

If there's a destruction race with kref_put() the barrier won't

Sorry, could you say what the destruction race is?

The race to 0-refs, iow. the case above when you assume both cases start out with 1 ref.

#16 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 9 lines · Peter Zijlstra
Quoted 7 lines · Ming Lei

CPU0                  CPU1

atomic_set(v)
smp_mb()
                              smp_mb() atomic_dec_and_test(v)

Quoted 5 lines
Quoted 2 lines

Without the barrier after atomic_set, CPU1 may see a stale value of v first, then decrease it, so may miss a release operation.

Your example is doubly broken. If there's concurrency possible with atomic_set() you've lost.

kref_init is guaranteed to be run only one time __before__ executing kref_get/kref_put.

Quoted 8 lines

Lets change it to kref_get() aka atomic_inc():

       CPU0            CPU1

       atomic_inc()
                       atomic_dec_and_test()

Quoted 6 lines

and

                       atomic_dec_and_test()
       atomic_inc()

For if the first is possible, then so is the second.

Yes, both are reasonable.

Quoted 5 lines

This illustrates that no matter how many barriers you put in, you're still up shit creek without no paddle because the kref_put() can come in before you do the kref_get(), making the kref_get() the invalid operation.

So one smp_mb__before_atomic_inc should be added before atomic_inc to make sure that CPU0 can see the uptodate ref, right?

Quoted 5 lines
Quoted 2 lines

The pair of smp_mb can make order between atomic_set and atomic_dec_and_test, can't it?

No. Because there's nothing stopping the dec from happening before the set/inc.

As stated above, one smp_mb__before_atomic_inc before atomic_inc may avoid the race.

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

If there's a destruction race with kref_put() the barrier won't

Sorry, could you say what the destruction race is?

The race to 0-refs, iow. the case above when you assume both cases start out with 1 ref.

But the initial value of kref is 1, so seems we don't need to consider the 0-refs.

thanks,

Signature · 1 line
Ming Lei
#17 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 10 lines · Ming Lei
Quoted 9 lines · Peter Zijlstra
Quoted 7 lines · Ming Lei

CPU0 CPU1

atomic_set(v)
smp_mb()

                              smp_mb()
                              atomic_dec_and_test(v)
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

Without the barrier after atomic_set, CPU1 may see a stale value of v first, then decrease it, so may miss a release operation.

Your example is doubly broken. If there's concurrency possible with atomic_set() you've lost.

kref_init is guaranteed to be run only one time __before__ executing kref_get/kref_put.

If used properly, yes. But in that case you still don't need the barrier. Whatever means you use to make the object visible to other CPUs will include a barrier.

Quoted 17 lines
Quoted 7 lines

Lets change it to kref_get() aka atomic_inc():

CPU0 CPU1

atomic_inc()
atomic_dec_and_test()

Quoted 6 lines

and

atomic_dec_and_test()
atomic_inc()

For if the first is possible, then so is the second.

Yes, both are reasonable.

Quoted 1 line
Quoted 7 lines
Quoted 4 lines

This illustrates that no matter how many barriers you put in, you're still up shit creek without no paddle because the kref_put() can come in before you do the kref_get(), making the kref_get() the invalid operation.

So one smp_mb__before_atomic_inc should be added before atomic_inc to make sure that CPU0 can see the uptodate ref, right?

No.

Assume v == 1:

	CPU0		CPU1

			atomic_dec_and_test(); /* --v == 0 */
				kfree()

	smp_mb__before_atomic_inc()
	atomic_inc(); <-- OOPS!

You still got an access to already freed memory. There is no amount of memory barriers that will solve this problem.

Quoted 2 lines

But the initial value of kref is 1, so seems we don't need to consider the 0-refs.

There's a dec in there, isn't it. How much is 1-1?

#18 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 58 lines · Peter Zijlstra
Quoted 10 lines · Ming Lei
Quoted 9 lines · Peter Zijlstra
Quoted 7 lines · Ming Lei

CPU0                  CPU1

atomic_set(v)
smp_mb()
                              smp_mb() atomic_dec_and_test(v)

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

Without the barrier after atomic_set, CPU1 may see a stale value of v first, then decrease it, so may miss a release operation.

Your example is doubly broken. If there's concurrency possible with atomic_set() you've lost.

kref_init is guaranteed to be run only one time __before__ executing kref_get/kref_put.

If used properly, yes. But in that case you still don't need the barrier. Whatever means you use to make the object visible to other CPUs will include a barrier.

Quoted 17 lines
Quoted 7 lines

Lets change it to kref_get() aka atomic_inc():

       CPU0            CPU1

       atomic_inc()
                       atomic_dec_and_test()

Quoted 6 lines

and

                       atomic_dec_and_test()
       atomic_inc()

For if the first is possible, then so is the second.

Yes, both are reasonable.

Quoted 1 line
Quoted 7 lines
Quoted 4 lines

This illustrates that no matter how many barriers you put in, you're still up shit creek without no paddle because the kref_put() can come in before you do the kref_get(), making the kref_get() the invalid operation.

So one smp_mb__before_atomic_inc should be added before atomic_inc to make sure that CPU0 can see the uptodate ref, right?

No.

Assume v == 1:

       CPU0            CPU1

                       atomic_dec_and_test(); /* --v == 0 */ kfree()

Quoted 6 lines

       smp_mb__before_atomic_inc()
       atomic_inc(); <-- OOPS!

You still got an access to already freed memory. There is no amount of memory barriers that will solve this problem.

Yes, kref is designed as so, and it is nothing to do with memory barrier, and more like a logical issue.

The implicit rule about kref is that use should make sure that kref can not be touched once it is released.

thanks,

Signature · 1 line
Ming Lei
#19 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 3 lines · Ming Lei

The implicit rule about kref is that use should make sure that kref can not be touched once it is released.

The only transition for with order makes any difference what so ever is 1 -> 0, you just said that should be done by external means (I agree and have argued thusly), therefore any memory barriers outside of these external means are superfluous.

Thus the proposed patch is correct.

#20 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 11 lines · Peter Zijlstra
Quoted 3 lines · Ming Lei

The implicit rule about kref is that use should make sure that kref can not be touched once it is released.

The only transition for with order makes any difference what so ever is 1 -> 0, you just said that should be done by external means (I agree and have argued thusly), therefore any memory barriers outside of these external means are superfluous.

Thus the proposed patch is correct.

Also, this was an entirely different issue than was raised in the original changelog. Memory barriers in general are about ordering visibility between multiple operations done on one cpu vs them being visible on another.

The important point being that there need to be multiple operations on one cpu in order for them to be able to make a difference. And the whole issue in the past few emails only had a single operation on each cpu. Therefore memory barriers are completely irrelevant.

Ever so more because atomic_t is atomic on the variable regardless of visibility by a second party.

Anyway, one more way to illustrate my point; there's three distinct phases in the life cycle of a kref managed object: insert, lookup and removal. They are as follows:

INSERT:

	obj = alloc_obj();
	init_obj(obj);
	  kref_set(&obj->ref, 1);

	LOCK
	insert(obj);
	UNLOCK

LOOKUP:

	LOCK
	obj = lookup(key);
	if (obj)
	  kref_get();
	UNLOCK

	...

	kref_put(); /* assuming obj != NULL */

REMOVAL: /* assumes obj was acquired using a preceding LOOKUP */

	LOCK
	remove(obj);
	UNLOCK

	kref_sub(&obj->ref, 2); /* lookup + insert */

Before the insert() our obj isn't visible to other CPUs doing lookup(), therefore there can be no concurrency on the kref, after insert the insert's UNLOCK + lookup's LOCK provide the full memory barrier separating the last write to the object and the first read of it.

Multiple lookup()s can be concurrent with each other and the last remove(). Concurrent lookups are completely non-interesting since they can't ever trigger the ->0 transition.

A lookup interleaved with a removal is serialized on the lock around our data-structure, after the removal no new lookups will succeed, and thus no new kref_get()s will be issued and all that happens is decrements until we hit 0.

The issue from the original changelog was that within a lookup, reads and writes to the obj might be re-ordered with the acquisition of the refcount. IOW. something like:

  LOCK
  obj = lookup(); /* lets assume obj != NULL */
  kref_get(&obj->ref);
  UNLOCK

  value = obj->member;

  kref_put(&obj->ref);

Now, under our memory model, the read from obj->member can both happen, or be observed to happen before the increment from kref_get() is processed.

I completely fail to see how that is an issue, nor when it is, why kref should care. If you rely on that, you're doing something weird and exotic and had better place the appropriate memory barriers yourself.

#21 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 11 lines · Peter Zijlstra
Quoted 10 lines · Peter Zijlstra
Quoted 3 lines · Ming Lei

The implicit rule about kref is that use should make sure that kref can not be touched once it is released.

The only transition for with order makes any difference what so ever is 1 -> 0, you just said that should be done by external means (I agree and have argued thusly), therefore any memory barriers outside of these external means are superfluous.

Quoted 6 lines
Quoted 1 line

Thus the proposed patch is correct.

Also, this was an entirely different issue than was raised in the original changelog. Memory barriers in general are about ordering visibility between multiple operations done on one cpu vs them being visible on another.

For kref, maybe it is still multiple operations done on one cpu vs them being visible on another, but seems a bit implicit, see the common kref usage below:

CPU0 CPU1

					A:kref_init(&obj->ref)

B:kref_get(&obj->ref)
C:access obj E:access obj

					F:kref_put(&ojb->ref)

D:kref_put(obj->ref)

There is one smp_mb between E and F on CPU1, and we still need another one between B and C on CPU 0 to keep the order. Otherwise, CPU1 may observe out of order about B and C, then make obj released (F)too early, and cause OOPS on CPU 0.

Quoted 8 lines

The important point being that there need to be multiple operations on one cpu in order for them to be able to make a difference. And the whole issue in the past few emails only had a single operation on each cpu. Therefore memory barriers are completely irrelevant.

Ever so more because atomic_t is atomic on the variable regardless of visibility by a second party.

Maybe not, see above.

Quoted 23 lines

Anyway, one more way to illustrate my point; there's three distinct phases in the life cycle of a kref managed object: insert, lookup and removal. They are as follows:

INSERT:
       obj = alloc_obj();
       init_obj(obj);
         kref_set(&obj->ref, 1);

       LOCK
       insert(obj);
       UNLOCK

LOOKUP:
       LOCK
       obj = lookup(key);
       if (obj)
         kref_get();
       UNLOCK

       ...

Quoted 8 lines

       kref_put(); /* assuming obj != NULL */

REMOVAL: /* assumes obj was acquired using a preceding LOOKUP */ LOCK
       remove(obj);
       UNLOCK

       kref_sub(&obj->ref, 2); /* lookup + insert */

Looks like should be kref_sub(1) since LOOKUP may handle by itself.

Quoted 4 lines

Before the insert() our obj isn't visible to other CPUs doing lookup(), therefore there can be no concurrency on the kref, after insert the insert's UNLOCK + lookup's LOCK provide the full memory barrier

We can't assume any lock about kref usage, because kref is often used in lockless scenario.

Quoted 20 lines

separating the last write to the object and the first read of it.

Multiple lookup()s can be concurrent with each other and the last remove(). Concurrent lookups are completely non-interesting since they can't ever trigger the ->0 transition.

A lookup interleaved with a removal is serialized on the lock around our data-structure, after the removal no new lookups will succeed, and thus no new kref_get()s will be issued and all that happens is decrements until we hit 0.

The issue from the original changelog was that within a lookup, reads and writes to the obj might be re-ordered with the acquisition of the refcount. IOW. something like:

 LOCK
 obj = lookup(); /* lets assume obj != NULL */
 kref_get(&obj->ref);
 UNLOCK

Quoted 7 lines

 value = obj->member;

 kref_put(&obj->ref);

Now, under our memory model, the read from obj->member can both happen, or be observed to happen before the increment from kref_get() is processed.

It should be the problem if another CPU observed that write/read obj is done before kref_get.

Quoted 5 lines

I completely fail to see how that is an issue, nor when it is, why kref should care. If you rely on that, you're doing something weird and exotic and had better place the appropriate memory barriers yourself.

thanks,

Signature · 1 line
Ming Lei
#22 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 6 lines · Ming Lei

For kref, maybe it is still multiple operations done on one cpu vs them being visible on another, but seems a bit implicit, see the common kref usage below:

CPU0 CPU1

                                        A:kref_init(&obj->ref)

how does CPU0 get a ref to obj?

Quoted 4 lines

B:kref_get(&obj->ref)
C:access obj E:access obj

                                        F:kref_put(&ojb->ref)

D:kref_put(obj->ref)

#23 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 9 lines · Peter Zijlstra
Quoted 6 lines · Ming Lei

For kref, maybe it is still multiple operations done on one cpu vs them being visible on another, but seems a bit implicit, see the common kref usage below:

CPU0                            CPU1 A:kref_init(&obj->ref)

how does CPU0 get a ref to obj?

Suppose open/close/read/.. context is run on CPU0, and driver .probe/.release context(hotplug context) is run on CPU1. There are a few examples on usb driver(eg. usb-skeleton.c, ...)

Quoted 5 lines
Quoted 5 lines

B:kref_get(&obj->ref)
C:access obj                    E:access obj F:kref_put(&ojb->ref) D:kref_put(obj->ref)

thanks,

Signature · 1 line
Ming Lei
#24 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 6 lines · Ming Lei
Quoted 5 lines · Peter Zijlstra
Quoted 4 lines · Ming Lei

For kref, maybe it is still multiple operations done on one cpu vs them being visible on another, but seems a bit implicit, see the common kref usage below:

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

CPU0 CPU1

                                        A:kref_init(&obj->ref)

how does CPU0 get a ref to obj?

Suppose open/close/read/.. context is run on CPU0, and driver .probe/.release context(hotplug context) is run on CPU1. There are a few examples on usb driver(eg. usb-skeleton.c, ...)

I don't know the driver model, and I don't plan to start learning it now. But if what you said is possible its broken and no memory barriers will fix it.

#25 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 3 lines · Peter Zijlstra

I don't know the driver model, and I don't plan to start learning it now. But if what you said is possible its broken and no memory barriers will fix it.

IMO, you don't need to learn it, and my example is very simple and common kref usage in device drivers, :-)

Could we only focus on it and see what is problem? and why won't memory barrier fix it?

Anyway, device drivers are the most consumers of kref...

thanks,

Signature · 1 line
Ming Lei
#26 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 8 lines · Ming Lei
Quoted 3 lines · Peter Zijlstra

I don't know the driver model, and I don't plan to start learning it now. But if what you said is possible its broken and no memory barriers will fix it.

IMO, you don't need to learn it, and my example is very simple and common kref usage in device drivers, :-)

One needs to know the callchains and device driver core code leading up to those .open, .close, .read, .probe and .release callbacks. That's a lot of code.

Also, non of that is relevant. The kref interface is very simple. kref_set cannot and should not have any concurrency.

It it not for kref_set() to finalize the object (there is no requirement kref_set is the last operation before you publish the object).

As per Oliver's reply, your USB example uses usb_register_dev() to pushlish the object and that includes the LOCK and UNLOCK from my example; it also illustrates you do need to know the entire device model in order to talk sensibly about these callbacks you mentioned.

You already said that external means should be used to ensure no kref_get() is issued after the final kref_put(). This too is only possible if you use locks one way or another and I bet that if you go look at the device model you'll find plenty around device_unregister().

These two constraints alone are enough to remove both memory barriers from the kref code.

#27 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 11 lines · Peter Zijlstra

One needs to know the callchains and device driver core code leading up to those .open, .close, .read, .probe and .release callbacks. That's a lot of code.

Also, non of that is relevant. The kref interface is very simple. kref_set cannot and should not have any concurrency.

It it not for kref_set() to finalize the object (there is no requirement kref_set is the last operation before you publish the object).

Quoted 7 lines

As per Oliver's reply, your USB example uses usb_register_dev() to pushlish the object and that includes the LOCK and UNLOCK from my example; it also illustrates you do need to know the entire device model in order to talk sensibly about these callbacks you mentioned.

You already said that external means should be used to ensure no kref_get() is issued after the final kref_put(). This too is only

Indeed, that is the famous open/disconnect race. In fact, there is the bug in usb-skeleton.c. Suppose usb device is disconnected between usb_get_intfdata() and kref_get() in skel_open(), kref_get may access a freed object.

Quoted 7 lines

possible if you use locks one way or another and I bet that if you go look at the device model you'll find plenty around device_unregister().

These two constraints alone are enough to remove both memory barriers from the kref code.

thanks,

Signature · 1 line
Ming Lei
#28 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

In reply to Ming Lei
Quoted 6 lines · Ming Lei
Quoted 3 lines · Peter Zijlstra

I don't know the driver model, and I don't plan to start learning it now. But if what you said is possible its broken and no memory barriers will fix it.

Quoted 7 lines

IMO, you don't need to learn it, and my example is very simple and common kref usage in device drivers, :-)

Could we only focus on it and see what is problem? and why won't memory barrier fix it?

Anyway, device drivers are the most consumers of kref...

Really, kref is not magical per itself.

Using kref (or any refcounting) doesnt avoid bugs.

In the example you gave, there is a clear bug :

CPU0 CPU1

                                        A:kref_init(&obj->ref)

B:kref_get(&obj->ref)
C:access obj E:access obj

                                        F:kref_put(&ojb->ref)

D:kref_put(obj->ref)

because _nothing_ prevents CPU0 accessing obj right after F:

To solve this problem, you need another way to synchronize accesses.

#29 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

In reply to Ming Lei

Am Montag, 12. Dezember 2011, 11:32:58 schrieb Ming Lei:

Quoted 11 lines · Peter Zijlstra
Quoted 4 lines

I don't know the driver model, and I don't plan to start learning it now. But if what you said is possible its broken and no memory barriers will fix it.

IMO, you don't need to learn it, and my example is very simple and common kref usage in device drivers, :-)

Could we only focus on it and see what is problem? and why won't memory barrier fix it?

You don't have a CPU ordering problem. If CPU A can do a kfree() you need to make sure CPU B doesn't get a pointer to that object. Basically your race is:

CPU A CPU B

p = a;
								p = a;
p->counter--;
if (!p->counter) kfree(p);
a = NULL;
								p->counter++;

This is not an ordering problem. You have a real critical section here. It doesn't matter when CPU B sees the decrement.
You must make sure there are no pointers to objects you might free if you intend to use the pointers without locks.

Regards
Oliver

#30 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

In reply to Ming Lei

Am Montag, 12. Dezember 2011, 10:57:31 schrieb Ming Lei:

Quoted 6 lines · Peter Zijlstra
Quoted 5 lines · Ming Lei
Quoted 4 lines

For kref, maybe it is still multiple operations done on one cpu vs them being visible on another, but seems a bit implicit, see the common kref usage below:

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

CPU0 CPU1

                                        A:kref_init(&obj->ref)

how does CPU0 get a ref to obj?

Suppose open/close/read/.. context is run on CPU0, and driver .probe/.release context(hotplug context) is run on CPU1. There are a few examples on usb driver(eg. usb-skeleton.c, ...)

USB generally relies on an implied barrier just as:

        /* we can register the device now, as it is ready */
        retval = usb_register_dev(interface, &skel_class);

Generally reference counting cannot help you if kfree() is involved

Regards
Oliver

#31 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 16 lines · Oliver Neukum

Am Montag, 12. Dezember 2011, 10:57:31 schrieb Ming Lei:

Quoted 6 lines · Peter Zijlstra
Quoted 5 lines · Ming Lei
Quoted 4 lines

For kref, maybe it is still multiple operations done on one cpu vs them being visible on another, but seems a bit implicit, see the common kref usage below:

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

CPU0 CPU1

                                        A:kref_init(&obj->ref)

how does CPU0 get a ref to obj?

Suppose open/close/read/.. context is run on CPU0, and driver .probe/.release context(hotplug context) is run on CPU1. There are a few examples on usb driver(eg. usb-skeleton.c, ...)

Quoted 6 lines

USB generally relies on an implied barrier just as:

        /* we can register the device now, as it is ready */
        retval = usb_register_dev(interface, &skel_class);

Generally reference counting cannot help you if kfree() is involved

So, Oliver, you don't have any objection to this patch removing the barriers in kref, right? Originally you added them, I just wanted to make sure before I applied this.

thanks,

greg k-h

#32 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Am Montag, 12. Dezember 2011, 20:30:40 schrieb Greg KH:

Quoted 19 lines · Oliver Neukum
Quoted 18 lines

Am Montag, 12. Dezember 2011, 10:57:31 schrieb Ming Lei:

Quoted 6 lines · Peter Zijlstra
Quoted 5 lines · Ming Lei
Quoted 4 lines

For kref, maybe it is still multiple operations done on one cpu vs them being visible on another, but seems a bit implicit, see the common kref usage below:

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

CPU0 CPU1

                                        A:kref_init(&obj->ref)

how does CPU0 get a ref to obj?

Suppose open/close/read/.. context is run on CPU0, and driver .probe/.release context(hotplug context) is run on CPU1. There are a few examples on usb driver(eg. usb-skeleton.c, ...)

USB generally relies on an implied barrier just as:

Quoted 8 lines
Quoted 4 lines
        /* we can register the device now, as it is ready */
        retval = usb_register_dev(interface, &skel_class);

Generally reference counting cannot help you if kfree() is involved

So, Oliver, you don't have any objection to this patch removing the barriers in kref, right? Originally you added them, I just wanted to make sure before I applied this.

I do not remember any more why I introduced this.

I guess I worried not about the increment, but the decrement. Which makes me wonder what happens if you don't intend to get the kref again, but need to make sure it is usually freed, like:

CPU A CPU B

kref_get(p)
start_io(p)

									[interrupt from IO]
									kref_put(p)

You need an ordering primitive between start_io() and kref_get() or the counter could go negative.
I think I was worried about it missing.

Regards
Oliver

#33 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 38 lines · Oliver Neukum

Am Montag, 12. Dezember 2011, 20:30:40 schrieb Greg KH:

Quoted 19 lines · Oliver Neukum
Quoted 18 lines

Am Montag, 12. Dezember 2011, 10:57:31 schrieb Ming Lei:

Quoted 6 lines · Peter Zijlstra
Quoted 5 lines · Ming Lei
Quoted 4 lines

For kref, maybe it is still multiple operations done on one cpu vs them being visible on another, but seems a bit implicit, see the common kref usage below:

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

CPU0 CPU1

                                        A:kref_init(&obj->ref)

how does CPU0 get a ref to obj?

Suppose open/close/read/.. context is run on CPU0, and driver .probe/.release context(hotplug context) is run on CPU1. There are a few examples on usb driver(eg. usb-skeleton.c, ...)

USB generally relies on an implied barrier just as:

Quoted 8 lines
Quoted 4 lines
        /* we can register the device now, as it is ready */
        retval = usb_register_dev(interface, &skel_class);

Generally reference counting cannot help you if kfree() is involved

So, Oliver, you don't have any objection to this patch removing the barriers in kref, right? Originally you added them, I just wanted to make sure before I applied this.

I do not remember any more why I introduced this.

I guess I worried not about the increment, but the decrement. Which makes me wonder what happens if you don't intend to get the kref again, but need to make sure it is usually freed, like:

CPU A CPU B

Quoted 7 lines

kref_get(p)
start_io(p)

									[interrupt from IO]
									kref_put(p)

You need an ordering primitive between start_io() and kref_get() or the counter could go negative.

Really? On an atomic variable? I didn't think this was needed for atomics to ensure this type of thing couldn't happen.

Quoted 1 line

I think I was worried about it missing.

I'd be worried as well, but I don't think that can really happen, or am I totally wrong?

greg k-h

#34 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Am Dienstag, 13. Dezember 2011, 00:14:19 schrieb Greg KH:

Quoted 12 lines
Quoted 12 lines

I guess I worried not about the increment, but the decrement. Which makes me wonder what happens if you don't intend to get the kref again, but need to make sure it is usually freed, like:

CPU A CPU B

kref_get(p)
start_io(p)
[interrupt from IO] kref_put(p)

Quoted 5 lines
Quoted 2 lines

You need an ordering primitive between start_io() and kref_get() or the counter could go negative.

Really? On an atomic variable? I didn't think this was needed for atomics to ensure this type of thing couldn't happen.

If you use an atomic variable you can be sure that the result will be mathematically correct, even if you touch the variable from many CPUs. (with add & sub of course) That is, refering to that variable.

It does not guarantee ordering

CPU A CPU B
atomic_set(&a, 1);
atomic_set(&b, 1);

atomic_set(&c, 1);
									while (!atomic_read(&c));
									d = atomic_read(&a) + atomic_read(&b);

is asking for trouble. You need to do:

CPU A CPU B
atomic_set(&a, 1);
atomic_set(&b, 1);
smp_wmb();

atomic_set(&c, 1);
									while (!atomic_read(&c));
									smp_rmb();
									d = atomic_read(&a) + atomic_read(&b);

Now replace c with an interrupt and you see the problem. It definitely exists, but my solution was quite bad. The wmb() must be in start_io() in the first example I gave. Putting it into kref was the wrong place.

Regards
Oliver

PS: even in the example I first gave the result will eventually be 0. But that is useless because the check for zero is done only in kref_put()

Signature · 6 lines
- - - 
SUSE LINUX Products GmbH, GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer, HRB 16746 (AG Nürnberg) 
Maxfeldstraße 5                         
90409 Nürnberg 
Germany 
- - - 
#35 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

In reply to Oliver Neukum
Quoted 11 lines · Oliver Neukum

I guess I worried not about the increment, but the decrement. Which makes me wonder what happens if you don't intend to get the kref again, but need to make sure it is usually freed, like:

CPU A CPU B

kref_get(p)
start_io(p)
[interrupt from IO] kref_put(p)

I would expect that if something was needed here, the io stack would provide the barriers since the io completion will probably want to change state set by the start_io thing.

Anyway, I would put this squarely outside the responsibility of kref.

#36 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Am Dienstag, 13. Dezember 2011, 10:12:20 schrieb Peter Zijlstra:

Quoted 13 lines · Oliver Neukum
Quoted 11 lines

I guess I worried not about the increment, but the decrement. Which makes me wonder what happens if you don't intend to get the kref again, but need to make sure it is usually freed, like:

CPU A CPU B

kref_get(p)
start_io(p)
[interrupt from IO] kref_put(p)

Quoted 5 lines

I would expect that if something was needed here, the io stack would provide the barriers since the io completion will probably want to change state set by the start_io thing.

Anyway, I would put this squarely outside the responsibility of kref.

I agree, so let's remove it.

Regards

#37 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

In reply to Ming Lei
Quoted 2 lines · Ming Lei

We can't assume any lock about kref usage, because kref is often used in lockless scenario.

Then you're doing it wrong, seriously.

kref in lockless scenarios simply doesn't work, you need rcu (or another means to stabilize storage) and atomic_inc_not_zero() as well as means to validate your object after the lookup.

#38 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 9 lines · Peter Zijlstra
Quoted 2 lines · Ming Lei

We can't assume any lock about kref usage, because kref is often used in lockless scenario.

Then you're doing it wrong, seriously.

kref in lockless scenarios simply doesn't work, you need rcu (or another means to stabilize storage) and atomic_inc_not_zero() as well as means to validate your object after the lookup.

I agree, and I'll apply your patches later today, thanks for doing them.

greg k-h

#39 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

In reply to Ming Lei
Quoted 7 lines · Ming Lei
Quoted 7 lines

LOCK
obj = lookup(); /* lets assume obj != NULL */
kref_get(&obj->ref);
UNLOCK

value = obj->member;

Quoted 8 lines
Quoted 5 lines

kref_put(&obj->ref);

Now, under our memory model, the read from obj->member can both happen, or be observed to happen before the increment from kref_get() is processed.

It should be the problem if another CPU observed that write/read obj is done before kref_get.

WHY?!?!?!

#40 lore raw

Re: [PATCH 3/3] kref: Remove the memory barriers

Quoted 10 lines · Peter Zijlstra
Quoted 9 lines · Ming Lei
Quoted 9 lines

 LOCK
 obj = lookup(); /* lets assume obj != NULL */
 kref_get(&obj->ref);
 UNLOCK

 value = obj->member;

 kref_put(&obj->ref);

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

Now, under our memory model, the read from obj->member can both happen, or be observed to happen before the increment from kref_get() is processed.

It should be the problem if another CPU observed that write/read obj is done before kref_get.

WHY?!?!?!

I did't say the reason because it is same with the example I explained in the same reply. So if you can make me enlightened on this example, all will be OK, :-)

thanks,

Signature · 1 line
Ming Lei