linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH] <linux/taggedptr.h>: Introduce tagged pointer
@ 2018-06-28  9:06 Gao Xiang
  2018-06-28  9:23 ` Matthew Wilcox
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Gao Xiang @ 2018-06-28  9:06 UTC (permalink / raw)
  To: viro
  Cc: Gao Xiang, Greg Kroah-Hartman, Kate Stewart, Philippe Ombredanne,
	Thomas Gleixner, Chao Yu, Miao Xie, linux-fsdevel, linux-erofs,
	linux-kernel

Currently kernel has scattered tagged pointer usages hacked
by hand in plain code, without a unique and portable functionset
to highlight the tagged pointer itself and wrap these hacked code
in order to clean up all over meaningless magic masks.

Therefore, this patch introduces simple generic methods to fold
tags into a pointer integer. It currently reuses the last 2 bits
of the pointer for tags, which are safely for all modern platforms.

In addition, it will also be used for the upcoming EROFS filesystem,
which heavily uses tagged pointer approach for high performance
and reducing extra memory allocation.

Refer to:
https://en.wikipedia.org/wiki/Tagged_pointer

To: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Kate Stewart <kstewart@linuxfoundation.org>
Cc: Philippe Ombredanne <pombredanne@nexb.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Chao Yu <yuchao0@huawei.com>
Cc: Miao Xie <miaoxie@huawei.com>
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-erofs@lists.ozlabs.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
---

Tagged pointers are good for performance and memory saving,
hoping for a generic taggedptr approach to clean up redundant dirty code.

Any comments, suggestions or alternative approaches are welcomed. :)

Thanks,
Gao Xiang

 fs/file.c                 | 19 ++++++------
 include/linux/file.h      |  7 +++--
 include/linux/taggedptr.h | 76 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 90 insertions(+), 12 deletions(-)
 create mode 100644 include/linux/taggedptr.h

diff --git a/fs/file.c b/fs/file.c
index 7ffd6e9..6d6f185 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -727,7 +727,7 @@ struct file *fget_raw(unsigned int fd)
  * The fput_needed flag returned by fget_light should be passed to the
  * corresponding fput_light.
  */
-static unsigned long __fget_light(unsigned int fd, fmode_t mask)
+static taggedptr_t __fget_light(unsigned int fd, fmode_t mask)
 {
 	struct files_struct *files = current->files;
 	struct file *file;
@@ -736,33 +736,34 @@ static unsigned long __fget_light(unsigned int fd, fmode_t mask)
 		file = __fcheck_files(files, fd);
 		if (!file || unlikely(file->f_mode & mask))
 			return 0;
-		return (unsigned long)file;
+		return taggedptr_fold(file, 0);
 	} else {
 		file = __fget(fd, mask);
 		if (!file)
 			return 0;
-		return FDPUT_FPUT | (unsigned long)file;
+		return taggedptr_fold(file, FDPUT_FPUT);
 	}
 }
-unsigned long __fdget(unsigned int fd)
+
+taggedptr_t __fdget(unsigned int fd)
 {
 	return __fget_light(fd, FMODE_PATH);
 }
 EXPORT_SYMBOL(__fdget);
 
-unsigned long __fdget_raw(unsigned int fd)
+taggedptr_t __fdget_raw(unsigned int fd)
 {
 	return __fget_light(fd, 0);
 }
 
-unsigned long __fdget_pos(unsigned int fd)
+taggedptr_t __fdget_pos(unsigned int fd)
 {
-	unsigned long v = __fdget(fd);
-	struct file *file = (struct file *)(v & ~3);
+	taggedptr_t v = __fdget(fd);
+	struct file *file = taggedptr_unfold_ptr(v);
 
 	if (file && (file->f_mode & FMODE_ATOMIC_POS)) {
 		if (file_count(file) > 1) {
-			v |= FDPUT_POS_UNLOCK;
+			taggedptr_set_tags(v, FDPUT_POS_UNLOCK);
 			mutex_lock(&file->f_pos_lock);
 		}
 	}
diff --git a/include/linux/file.h b/include/linux/file.h
index 279720d..af81f68 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h
@@ -9,6 +9,7 @@
 #include <linux/compiler.h>
 #include <linux/types.h>
 #include <linux/posix_types.h>
+#include <linux/taggedptr.h>
 
 struct file;
 
@@ -42,9 +43,9 @@ static inline void fdput(struct fd fd)
 
 extern struct file *fget(unsigned int fd);
 extern struct file *fget_raw(unsigned int fd);
-extern unsigned long __fdget(unsigned int fd);
-extern unsigned long __fdget_raw(unsigned int fd);
-extern unsigned long __fdget_pos(unsigned int fd);
+extern taggedptr_t __fdget(unsigned int fd);
+extern taggedptr_t __fdget_raw(unsigned int fd);
+extern taggedptr_t __fdget_pos(unsigned int fd);
 extern void __f_unlock_pos(struct file *);
 
 static inline struct fd __to_fd(unsigned long v)
diff --git a/include/linux/taggedptr.h b/include/linux/taggedptr.h
new file mode 100644
index 0000000..1805394
--- /dev/null
+++ b/include/linux/taggedptr.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Tagged pointer implementation
+ *
+ * Copyright (C) 2018 Gao Xiang <gaoxiang25@huawei.com>
+ */
+#ifndef _LINUX_TAGGEDPTR_H
+#define _LINUX_TAGGEDPTR_H
+
+#include <linux/types.h>
+#include <linux/build_bug.h>
+
+/*
+ * mark these special integers as another type
+ * in order to highlight the tagged pointer usage.
+ */
+typedef uintptr_t	taggedptr_t;
+
+/*
+ * generally for all architectures, the last 2 bits of
+ * pointer can be used safely
+ */
+#ifndef TAGGEDPTR_TAGS_BITS
+#define TAGGEDPTR_TAGS_BITS	2
+#endif
+
+#define TAGGEDPTR_TAGS_MASK	((1 << TAGGEDPTR_TAGS_BITS) - 1)
+
+extern void __compiletime_error("bad taggedptr tags")
+	__bad_taggedptr_tags(void);
+
+/* encode the tagged pointer */
+static inline taggedptr_t taggedptr_fold(void *ptr, unsigned int tags)
+{
+	if (__builtin_constant_p(tags) && (tags & ~TAGGEDPTR_TAGS_MASK))
+		__bad_taggedptr_tags();
+
+	return (taggedptr_t)ptr | tags;
+}
+
+static inline void *taggedptr_unfold_ptr(taggedptr_t tptr)
+{
+	return (void *)(tptr & ~TAGGEDPTR_TAGS_MASK);
+}
+
+static inline unsigned int taggedptr_unfold_tags(taggedptr_t tptr)
+{
+	return tptr & TAGGEDPTR_TAGS_MASK;
+}
+
+static inline taggedptr_t taggedptr_replace_tags(taggedptr_t tptr,
+						 unsigned int tags)
+{
+	return taggedptr_fold(taggedptr_unfold_ptr(tptr), tags);
+}
+
+static inline taggedptr_t taggedptr_set_tags(taggedptr_t tptr,
+					     unsigned int tags)
+{
+	if (__builtin_constant_p(tags) && (tags & ~TAGGEDPTR_TAGS_MASK))
+		__bad_taggedptr_tags();
+
+	return tptr |= tags;
+}
+
+static inline taggedptr_t taggedptr_clear_tags(taggedptr_t tptr,
+					       unsigned int tags)
+{
+	if (__builtin_constant_p(tags) && (tags & ~TAGGEDPTR_TAGS_MASK))
+		__bad_taggedptr_tags();
+
+	return tptr &= ~tags;
+}
+
+#endif
+
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [RFC PATCH] <linux/taggedptr.h>: Introduce tagged pointer
  2018-06-28  9:06 [RFC PATCH] <linux/taggedptr.h>: Introduce tagged pointer Gao Xiang
@ 2018-06-28  9:23 ` Matthew Wilcox
  2018-06-28  9:40   ` Gao Xiang
  2018-06-28 14:17 ` [RFC PATCH v2] <linux/tagptr.h>: " Gao Xiang
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 6+ messages in thread
From: Matthew Wilcox @ 2018-06-28  9:23 UTC (permalink / raw)
  To: Gao Xiang
  Cc: viro, Greg Kroah-Hartman, Kate Stewart, Philippe Ombredanne,
	Thomas Gleixner, Chao Yu, Miao Xie, linux-fsdevel, linux-erofs,
	linux-kernel

On Thu, Jun 28, 2018 at 05:06:29PM +0800, Gao Xiang wrote:
> Therefore, this patch introduces simple generic methods to fold
> tags into a pointer integer. It currently reuses the last 2 bits
> of the pointer for tags, which are safely for all modern platforms.

The m68k people will have your head.  alignof(unsigned long) == 2 on
m68k.  Now, kmalloc always returns 8-byte aligned quantities, but
if you have:

static unsigned long foo;

then ((unsigned long)&foo & 2) may be non-zero.

> +/*
> + * mark these special integers as another type
> + * in order to highlight the tagged pointer usage.
> + */
> +typedef uintptr_t	taggedptr_t;

I find this a bit verbose.  How about tagptr_t ?


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC PATCH] <linux/taggedptr.h>: Introduce tagged pointer
  2018-06-28  9:23 ` Matthew Wilcox
@ 2018-06-28  9:40   ` Gao Xiang
  0 siblings, 0 replies; 6+ messages in thread
From: Gao Xiang @ 2018-06-28  9:40 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: viro, Greg Kroah-Hartman, Kate Stewart, Philippe Ombredanne,
	Thomas Gleixner, Chao Yu, Miao Xie, linux-fsdevel, linux-erofs,
	linux-kernel

Hi Matthew,

On 2018/6/28 17:23, Matthew Wilcox wrote:
> On Thu, Jun 28, 2018 at 05:06:29PM +0800, Gao Xiang wrote:
>> Therefore, this patch introduces simple generic methods to fold
>> tags into a pointer integer. It currently reuses the last 2 bits
>> of the pointer for tags, which are safely for all modern platforms.
> 
> The m68k people will have your head.  alignof(unsigned long) == 2 on
> m68k.  Now, kmalloc always returns 8-byte aligned quantities, but
> if you have:
> 
> static unsigned long foo;
> 
> then ((unsigned long)&foo & 2) may be non-zero.
> 

Oh.. I missed it. How about covering dynamic allocation scenario only?
(since 1 bit is too limited to be used for generic purposes... :( )

Or generate different bit mask versions by using macro? --- I could try in the next patch.

Anyway, I think it is depended on specific use cases and corresponding architectures...

>> +/*
>> + * mark these special integers as another type
>> + * in order to highlight the tagged pointer usage.
>> + */
>> +typedef uintptr_t	taggedptr_t;
> 
> I find this a bit verbose.  How about tagptr_t ?
> 

Boths are ok for me :)

Thanks,
Gao Xiang


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC PATCH v2] <linux/tagptr.h>: Introduce tagged pointer
  2018-06-28  9:06 [RFC PATCH] <linux/taggedptr.h>: Introduce tagged pointer Gao Xiang
  2018-06-28  9:23 ` Matthew Wilcox
@ 2018-06-28 14:17 ` Gao Xiang
  2018-06-28 15:02 ` [RFC PATCH RESEND v3] " Gao Xiang
  2018-06-30  9:47 ` [RFC PATCH v4] " Gao Xiang
  3 siblings, 0 replies; 6+ messages in thread
From: Gao Xiang @ 2018-06-28 14:17 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Gao Xiang, Greg Kroah-Hartman, Kate Stewart, Matthew Wilcox,
	Philippe Ombredanne, Thomas Gleixner, Chao Yu, Miao Xie,
	linux-fsdevel, linux-erofs, linux-kernel

Currently kernel has scattered tagged pointer usages hacked
by hand in plain code, without a unique and portable functionset
to highlight the tagged pointer itself and wrap these hacked code
in order to clean up all over meaningless magic masks.

Therefore, this patch introduces simple generic methods to fold
tags into a pointer integer. It currently supports the last n bits
of the pointer for tags, which can be selected by users.

In addition, it will also be used for the upcoming EROFS filesystem,
which heavily uses tagged pointer approach for high performance
and reducing extra memory allocation.

Refer to:
https://en.wikipedia.org/wiki/Tagged_pointer

To: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Kate Stewart <kstewart@linuxfoundation.org>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Philippe Ombredanne <pombredanne@nexb.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Chao Yu <yuchao0@huawei.com>
Cc: Miao Xie <miaoxie@huawei.com>
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-erofs@lists.ozlabs.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
---
change log v2:
  - add support to select the last {1,2,3,...} bits for tags

Tagged pointers are good for performance and memory saving,
hoping for a generic taggedptr approach to clean up redundant dirty code.

Any comments, suggestions or alternative approaches are welcomed. :)

Thanks,
Gao Xiang

 fs/file.c              | 23 ++++++++-------
 include/linux/file.h   | 12 ++++----
 include/linux/tagptr.h | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 97 insertions(+), 16 deletions(-)
 create mode 100644 include/linux/tagptr.h

diff --git a/fs/file.c b/fs/file.c
index 7ffd6e9..5f1270b 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -727,7 +727,7 @@ struct file *fget_raw(unsigned int fd)
  * The fput_needed flag returned by fget_light should be passed to the
  * corresponding fput_light.
  */
-static unsigned long __fget_light(unsigned int fd, fmode_t mask)
+static tagptr2_t __fget_light(unsigned int fd, fmode_t mask)
 {
 	struct files_struct *files = current->files;
 	struct file *file;
@@ -735,34 +735,35 @@ static unsigned long __fget_light(unsigned int fd, fmode_t mask)
 	if (atomic_read(&files->count) == 1) {
 		file = __fcheck_files(files, fd);
 		if (!file || unlikely(file->f_mode & mask))
-			return 0;
-		return (unsigned long)file;
+			return tagptr_fold(tagptr2_t, NULL, 0);
+		return tagptr_fold(tagptr2_t, file, 0);
 	} else {
 		file = __fget(fd, mask);
 		if (!file)
-			return 0;
-		return FDPUT_FPUT | (unsigned long)file;
+			return tagptr_fold(tagptr2_t, NULL, 0);
+		return tagptr_fold(tagptr2_t, file, FDPUT_FPUT);
 	}
 }
-unsigned long __fdget(unsigned int fd)
+
+tagptr2_t __fdget(unsigned int fd)
 {
 	return __fget_light(fd, FMODE_PATH);
 }
 EXPORT_SYMBOL(__fdget);
 
-unsigned long __fdget_raw(unsigned int fd)
+tagptr2_t __fdget_raw(unsigned int fd)
 {
 	return __fget_light(fd, 0);
 }
 
-unsigned long __fdget_pos(unsigned int fd)
+tagptr2_t __fdget_pos(unsigned int fd)
 {
-	unsigned long v = __fdget(fd);
-	struct file *file = (struct file *)(v & ~3);
+	tagptr2_t v = __fdget(fd);
+	struct file *file = tagptr_unfold_ptr(v);
 
 	if (file && (file->f_mode & FMODE_ATOMIC_POS)) {
 		if (file_count(file) > 1) {
-			v |= FDPUT_POS_UNLOCK;
+			tagptr_set_tags(&v, FDPUT_POS_UNLOCK);
 			mutex_lock(&file->f_pos_lock);
 		}
 	}
diff --git a/include/linux/file.h b/include/linux/file.h
index 279720d..dbaa915 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h
@@ -9,6 +9,7 @@
 #include <linux/compiler.h>
 #include <linux/types.h>
 #include <linux/posix_types.h>
+#include <linux/tagptr.h>
 
 struct file;
 
@@ -42,14 +43,15 @@ static inline void fdput(struct fd fd)
 
 extern struct file *fget(unsigned int fd);
 extern struct file *fget_raw(unsigned int fd);
-extern unsigned long __fdget(unsigned int fd);
-extern unsigned long __fdget_raw(unsigned int fd);
-extern unsigned long __fdget_pos(unsigned int fd);
+extern tagptr2_t __fdget(unsigned int fd);
+extern tagptr2_t __fdget_raw(unsigned int fd);
+extern tagptr2_t __fdget_pos(unsigned int fd);
 extern void __f_unlock_pos(struct file *);
 
-static inline struct fd __to_fd(unsigned long v)
+static inline struct fd __to_fd(tagptr2_t v)
 {
-	return (struct fd){(struct file *)(v & ~3),v & 3};
+	return (struct fd){ tagptr_unfold_ptr(v),
+		tagptr_unfold_tags(v) };
 }
 
 static inline struct fd fdget(unsigned int fd)
diff --git a/include/linux/tagptr.h b/include/linux/tagptr.h
new file mode 100644
index 0000000..6905b51
--- /dev/null
+++ b/include/linux/tagptr.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Tagged pointer implementation
+ *
+ * Copyright (C) 2018 Gao Xiang <gaoxiang25@huawei.com>
+ */
+#ifndef _LINUX_TAGPTR_H
+#define _LINUX_TAGPTR_H
+
+#include <linux/types.h>
+#include <linux/build_bug.h>
+
+/* the name of types are tagptr{1, 2, 3...}_t */
+#define __MAKE_TAGPTR(n) \
+typedef struct tagptr##n {	\
+	uintptr_t v;	\
+} tagptr##n##_t;
+
+__MAKE_TAGPTR(1)
+__MAKE_TAGPTR(2)
+__MAKE_TAGPTR(3)
+__MAKE_TAGPTR(4)
+
+#undef __MAKE_TAGPTR
+
+extern void __compiletime_error("bad tagptr tags")
+	__bad_tagptr_tags(void);
+
+extern void __compiletime_error("bad tagptr type")
+	__bad_tagptr_type(void);
+
+#define __tagptr_mask_1(ptr, n)	\
+	__builtin_types_compatible_p(typeof(ptr), tagptr##n##_t) ? \
+		(1UL << (n)) - 1 :
+
+#define __tagptr_mask(ptr)	(\
+	__tagptr_mask_1(ptr, 1) ( \
+	__tagptr_mask_1(ptr, 2) ( \
+	__tagptr_mask_1(ptr, 3) ( \
+	__tagptr_mask_1(ptr, 4) ( \
+	__bad_tagptr_type(), 0)))))
+
+/* encode the tagged pointer */
+#define tagptr_fold(type, ptr, _tags) ({ \
+	const typeof(_tags) tags = (_tags); \
+	if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(type))) \
+		__bad_tagptr_tags(); \
+((typeof(type)){ .v = (uintptr_t)ptr | tags }); })
+
+#define tagptr_unfold_ptr(tptr) \
+	((void *)((tptr).v & ~__tagptr_mask(tptr)))
+
+#define tagptr_unfold_tags(tptr) \
+	((tptr).v & __tagptr_mask(tptr))
+
+#define tagptr_replace_tags(_ptptr, tags) ({ \
+	typeof(_ptptr) ptptr = (_ptptr); \
+	*ptptr = tagptr_fold(*ptptr, tagptr_unfold_ptr(*ptptr), tags); \
+*ptptr; })
+
+#define tagptr_set_tags(_ptptr, _tags) ({ \
+	typeof(_ptptr) ptptr = (_ptptr); \
+	const typeof(_tags) tags = (_tags); \
+	if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(*ptptr))) \
+		__bad_tagptr_tags(); \
+	ptptr->v |= tags; \
+*ptptr; })
+
+#define tagptr_clear_tags(_ptptr, _tags) ({ \
+	typeof(_ptptr) ptptr = (_ptptr); \
+	const typeof(_tags) tags = (_tags); \
+	if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(*ptptr))) \
+		__bad_tagptr_tags(); \
+	ptptr->v &= ~tags; \
+*ptptr; })
+
+#endif
+
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC PATCH RESEND v3] <linux/tagptr.h>: Introduce tagged pointer
  2018-06-28  9:06 [RFC PATCH] <linux/taggedptr.h>: Introduce tagged pointer Gao Xiang
  2018-06-28  9:23 ` Matthew Wilcox
  2018-06-28 14:17 ` [RFC PATCH v2] <linux/tagptr.h>: " Gao Xiang
@ 2018-06-28 15:02 ` Gao Xiang
  2018-06-30  9:47 ` [RFC PATCH v4] " Gao Xiang
  3 siblings, 0 replies; 6+ messages in thread
From: Gao Xiang @ 2018-06-28 15:02 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Gao Xiang, Greg Kroah-Hartman, Kate Stewart, Matthew Wilcox,
	Philippe Ombredanne, Thomas Gleixner, Chao Yu, Miao Xie,
	linux-fsdevel, linux-erofs, linux-kernel

Currently kernel has scattered tagged pointer usages hacked
by hand in plain code, without a unique and portable functionset
to highlight the tagged pointer itself and wrap these hacked code
in order to clean up all over meaningless magic masks.

Therefore, this patch introduces simple generic methods to fold
tags into a pointer integer. It currently supports the last n bits
of the pointer for tags, which can be selected by users.

In addition, it will also be used for the upcoming EROFS filesystem,
which heavily uses tagged pointer approach for high performance
and reducing extra memory allocation.

Refer to:
https://en.wikipedia.org/wiki/Tagged_pointer

To: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Kate Stewart <kstewart@linuxfoundation.org>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Philippe Ombredanne <pombredanne@nexb.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Chao Yu <yuchao0@huawei.com>
Cc: Miao Xie <miaoxie@huawei.com>
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-erofs@lists.ozlabs.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
---
change log v3:
  - include/linux/file.h: define a unique alias fdtagptr_t (tagptr2_t)

change log v2:
  - add support to select the last {1,2,3,...} bits for tags


Tagged pointers are good for performance and memory saving,
hoping for a generic taggedptr approach to clean up redundant dirty code.

Any comments, suggestions or alternative approaches are welcomed. :)

Thanks,
Gao Xiang

 fs/file.c              | 24 +++++++++-------
 include/linux/file.h   | 15 ++++++----
 include/linux/tagptr.h | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 101 insertions(+), 16 deletions(-)
 create mode 100644 include/linux/tagptr.h

diff --git a/fs/file.c b/fs/file.c
index 7ffd6e9..a54a551 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -727,42 +727,44 @@ struct file *fget_raw(unsigned int fd)
  * The fput_needed flag returned by fget_light should be passed to the
  * corresponding fput_light.
  */
-static unsigned long __fget_light(unsigned int fd, fmode_t mask)
+static fdtagptr_t __fget_light(unsigned int fd, fmode_t mask)
 {
+	const fdtagptr_t nil = tagptr_fold(fdtagptr_t, NULL, 0);
 	struct files_struct *files = current->files;
 	struct file *file;
 
 	if (atomic_read(&files->count) == 1) {
 		file = __fcheck_files(files, fd);
 		if (!file || unlikely(file->f_mode & mask))
-			return 0;
-		return (unsigned long)file;
+			return nil;
+		return tagptr_fold(fdtagptr_t, file, 0);
 	} else {
 		file = __fget(fd, mask);
 		if (!file)
-			return 0;
-		return FDPUT_FPUT | (unsigned long)file;
+			return nil;
+		return tagptr_fold(fdtagptr_t, file, FDPUT_FPUT);
 	}
 }
-unsigned long __fdget(unsigned int fd)
+
+fdtagptr_t __fdget(unsigned int fd)
 {
 	return __fget_light(fd, FMODE_PATH);
 }
 EXPORT_SYMBOL(__fdget);
 
-unsigned long __fdget_raw(unsigned int fd)
+fdtagptr_t __fdget_raw(unsigned int fd)
 {
 	return __fget_light(fd, 0);
 }
 
-unsigned long __fdget_pos(unsigned int fd)
+fdtagptr_t __fdget_pos(unsigned int fd)
 {
-	unsigned long v = __fdget(fd);
-	struct file *file = (struct file *)(v & ~3);
+	fdtagptr_t v = __fdget(fd);
+	struct file *file = tagptr_unfold_ptr(v);
 
 	if (file && (file->f_mode & FMODE_ATOMIC_POS)) {
 		if (file_count(file) > 1) {
-			v |= FDPUT_POS_UNLOCK;
+			tagptr_set_tags(&v, FDPUT_POS_UNLOCK);
 			mutex_lock(&file->f_pos_lock);
 		}
 	}
diff --git a/include/linux/file.h b/include/linux/file.h
index 279720d..38b6641 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h
@@ -9,6 +9,7 @@
 #include <linux/compiler.h>
 #include <linux/types.h>
 #include <linux/posix_types.h>
+#include <linux/tagptr.h>
 
 struct file;
 
@@ -34,6 +35,9 @@ struct fd {
 #define FDPUT_FPUT       1
 #define FDPUT_POS_UNLOCK 2
 
+/* tagged pointer for fd */
+typedef tagptr2_t	fdtagptr_t;
+
 static inline void fdput(struct fd fd)
 {
 	if (fd.flags & FDPUT_FPUT)
@@ -42,14 +46,15 @@ static inline void fdput(struct fd fd)
 
 extern struct file *fget(unsigned int fd);
 extern struct file *fget_raw(unsigned int fd);
-extern unsigned long __fdget(unsigned int fd);
-extern unsigned long __fdget_raw(unsigned int fd);
-extern unsigned long __fdget_pos(unsigned int fd);
+extern fdtagptr_t __fdget(unsigned int fd);
+extern fdtagptr_t __fdget_raw(unsigned int fd);
+extern fdtagptr_t __fdget_pos(unsigned int fd);
 extern void __f_unlock_pos(struct file *);
 
-static inline struct fd __to_fd(unsigned long v)
+static inline struct fd __to_fd(fdtagptr_t v)
 {
-	return (struct fd){(struct file *)(v & ~3),v & 3};
+	return (struct fd){ tagptr_unfold_ptr(v),
+		tagptr_unfold_tags(v) };
 }
 
 static inline struct fd fdget(unsigned int fd)
diff --git a/include/linux/tagptr.h b/include/linux/tagptr.h
new file mode 100644
index 0000000..50d2f89
--- /dev/null
+++ b/include/linux/tagptr.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Tagged pointer implementation
+ *
+ * Copyright (C) 2018 Gao Xiang <gaoxiang25@huawei.com>
+ */
+#ifndef _LINUX_TAGPTR_H
+#define _LINUX_TAGPTR_H
+
+#include <linux/types.h>
+#include <linux/build_bug.h>
+
+/* the name of types are tagptr{1, 2, 3...}_t */
+#define __MAKE_TAGPTR(n) \
+typedef struct tagptr##n {	\
+	uintptr_t v;	\
+} tagptr##n##_t;
+
+__MAKE_TAGPTR(1)
+__MAKE_TAGPTR(2)
+__MAKE_TAGPTR(3)
+__MAKE_TAGPTR(4)
+
+#undef __MAKE_TAGPTR
+
+extern void __compiletime_error("bad tagptr tags")
+	__bad_tagptr_tags(void);
+
+extern void __compiletime_error("bad tagptr type")
+	__bad_tagptr_type(void);
+
+#define __tagptr_mask_1(ptr, n)	\
+	__builtin_types_compatible_p(typeof(ptr), tagptr##n##_t) ? \
+		(1UL << (n)) - 1 :
+
+#define __tagptr_mask(ptr)	(\
+	__tagptr_mask_1(ptr, 1) ( \
+	__tagptr_mask_1(ptr, 2) ( \
+	__tagptr_mask_1(ptr, 3) ( \
+	__tagptr_mask_1(ptr, 4) ( \
+	__bad_tagptr_type(), 0)))))
+
+/* encode the tagged pointer */
+#define tagptr_fold(type, ptr, _tags) ({ \
+	const typeof(_tags) tags = (_tags); \
+	if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(type))) \
+		__bad_tagptr_tags(); \
+((typeof(type)){ .v = (uintptr_t)ptr | tags }); })
+
+#define tagptr_unfold_ptr(tptr) \
+	((void *)((tptr).v & ~__tagptr_mask(tptr)))
+
+#define tagptr_unfold_tags(tptr) \
+	((tptr).v & __tagptr_mask(tptr))
+
+#define tagptr_replace_tags(_ptptr, tags) ({ \
+	typeof(_ptptr) ptptr = (_ptptr); \
+	*ptptr = tagptr_fold(*ptptr, tagptr_unfold_ptr(*ptptr), tags); \
+*ptptr; })
+
+#define tagptr_set_tags(_ptptr, _tags) ({ \
+	typeof(_ptptr) ptptr = (_ptptr); \
+	const typeof(_tags) tags = (_tags); \
+	if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(*ptptr))) \
+		__bad_tagptr_tags(); \
+	ptptr->v |= tags; \
+*ptptr; })
+
+#define tagptr_clear_tags(_ptptr, _tags) ({ \
+	typeof(_ptptr) ptptr = (_ptptr); \
+	const typeof(_tags) tags = (_tags); \
+	if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(*ptptr))) \
+		__bad_tagptr_tags(); \
+	ptptr->v &= ~tags; \
+*ptptr; })
+
+#endif
+
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC PATCH v4] <linux/tagptr.h>: Introduce tagged pointer
  2018-06-28  9:06 [RFC PATCH] <linux/taggedptr.h>: Introduce tagged pointer Gao Xiang
                   ` (2 preceding siblings ...)
  2018-06-28 15:02 ` [RFC PATCH RESEND v3] " Gao Xiang
@ 2018-06-30  9:47 ` Gao Xiang
  3 siblings, 0 replies; 6+ messages in thread
From: Gao Xiang @ 2018-06-30  9:47 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Gao Xiang, Greg Kroah-Hartman, Kate Stewart, Matthew Wilcox,
	Philippe Ombredanne, Thomas Gleixner, Chao Yu, Miao Xie,
	linux-fsdevel, linux-erofs, linux-kernel

Currently kernel has scattered tagged pointer usages hacked
by hand in plain code, without a unique and portable functionset
to highlight the tagged pointer itself and wrap these hacked code
in order to clean up all over meaningless magic masks.

Therefore, this patch introduces simple generic methods to fold
tags into a pointer integer. It currently supports the last n bits
of the pointer for tags, which can be selected by users.

In addition, it will also be used for the upcoming EROFS filesystem,
which heavily uses tagged pointer approach for high performance
and reducing extra memory allocation.

Refer to:
https://en.wikipedia.org/wiki/Tagged_pointer

To: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Kate Stewart <kstewart@linuxfoundation.org>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Philippe Ombredanne <pombredanne@nexb.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Chao Yu <yuchao0@huawei.com>
Cc: Miao Xie <miaoxie@huawei.com>
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-erofs@lists.ozlabs.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
---
change log v4:
  - fix that the potential usage "#define tagptr2_t tagptr3_t" is broken
  - add tagptr_init & tagptr_cast_ptr from/to raw data, tagptr_eq for
    comparsion, tagptr_cmpxchg for CAS
  - the corresponding EROFS user (still in development):
    https://lists.ozlabs.org/pipermail/linux-erofs/2018-June/000044.html

change log v3:
  - include/linux/file.h: define a unique alias fdtagptr_t (tagptr2_t)

change log v2:
  - add support to select the last {1,2,3,...} bits for tags


Tagged pointers are good for performance and memory saving,
hoping for a generic taggedptr approach to clean up redundant dirty code.

Any comments, suggestions or alternative approaches are welcomed. :)

Thanks,
Gao Xiang

 fs/file.c              |  24 ++++++-----
 include/linux/file.h   |  15 ++++---
 include/linux/tagptr.h | 110 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+), 16 deletions(-)
 create mode 100644 include/linux/tagptr.h

diff --git a/fs/file.c b/fs/file.c
index 7ffd6e9..c54cb50 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -727,42 +727,44 @@ struct file *fget_raw(unsigned int fd)
  * The fput_needed flag returned by fget_light should be passed to the
  * corresponding fput_light.
  */
-static unsigned long __fget_light(unsigned int fd, fmode_t mask)
+static fdtagptr_t __fget_light(unsigned int fd, fmode_t mask)
 {
+	const fdtagptr_t nil = tagptr_init(fdtagptr_t, NULL);
 	struct files_struct *files = current->files;
 	struct file *file;
 
 	if (atomic_read(&files->count) == 1) {
 		file = __fcheck_files(files, fd);
 		if (!file || unlikely(file->f_mode & mask))
-			return 0;
-		return (unsigned long)file;
+			return nil;
+		return tagptr_fold(fdtagptr_t, file, 0);
 	} else {
 		file = __fget(fd, mask);
 		if (!file)
-			return 0;
-		return FDPUT_FPUT | (unsigned long)file;
+			return nil;
+		return tagptr_fold(fdtagptr_t, file, FDPUT_FPUT);
 	}
 }
-unsigned long __fdget(unsigned int fd)
+
+fdtagptr_t __fdget(unsigned int fd)
 {
 	return __fget_light(fd, FMODE_PATH);
 }
 EXPORT_SYMBOL(__fdget);
 
-unsigned long __fdget_raw(unsigned int fd)
+fdtagptr_t __fdget_raw(unsigned int fd)
 {
 	return __fget_light(fd, 0);
 }
 
-unsigned long __fdget_pos(unsigned int fd)
+fdtagptr_t __fdget_pos(unsigned int fd)
 {
-	unsigned long v = __fdget(fd);
-	struct file *file = (struct file *)(v & ~3);
+	fdtagptr_t v = __fdget(fd);
+	struct file *file = tagptr_unfold_ptr(v);
 
 	if (file && (file->f_mode & FMODE_ATOMIC_POS)) {
 		if (file_count(file) > 1) {
-			v |= FDPUT_POS_UNLOCK;
+			tagptr_set_tags(&v, FDPUT_POS_UNLOCK);
 			mutex_lock(&file->f_pos_lock);
 		}
 	}
diff --git a/include/linux/file.h b/include/linux/file.h
index 279720d..e2bb489 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h
@@ -9,6 +9,7 @@
 #include <linux/compiler.h>
 #include <linux/types.h>
 #include <linux/posix_types.h>
+#include <linux/tagptr.h>
 
 struct file;
 
@@ -34,6 +35,9 @@ struct fd {
 #define FDPUT_FPUT       1
 #define FDPUT_POS_UNLOCK 2
 
+/* tagged pointer for fd */
+typedef tagptr2_t	fdtagptr_t;
+
 static inline void fdput(struct fd fd)
 {
 	if (fd.flags & FDPUT_FPUT)
@@ -42,14 +46,15 @@ static inline void fdput(struct fd fd)
 
 extern struct file *fget(unsigned int fd);
 extern struct file *fget_raw(unsigned int fd);
-extern unsigned long __fdget(unsigned int fd);
-extern unsigned long __fdget_raw(unsigned int fd);
-extern unsigned long __fdget_pos(unsigned int fd);
+extern fdtagptr_t __fdget(unsigned int fd);
+extern fdtagptr_t __fdget_raw(unsigned int fd);
+extern fdtagptr_t __fdget_pos(unsigned int fd);
 extern void __f_unlock_pos(struct file *);
 
-static inline struct fd __to_fd(unsigned long v)
+static inline struct fd __to_fd(fdtagptr_t v)
 {
-	return (struct fd){(struct file *)(v & ~3),v & 3};
+	return (struct fd){ tagptr_unfold_ptr(v),
+		tagptr_unfold_tags(v) };
 }
 
 static inline struct fd fdget(unsigned int fd)
diff --git a/include/linux/tagptr.h b/include/linux/tagptr.h
new file mode 100644
index 0000000..b5c6016
--- /dev/null
+++ b/include/linux/tagptr.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Tagged pointer implementation
+ *
+ * Copyright (C) 2018 Gao Xiang <gaoxiang25@huawei.com>
+ */
+#ifndef _LINUX_TAGPTR_H
+#define _LINUX_TAGPTR_H
+
+#include <linux/types.h>
+#include <linux/build_bug.h>
+
+/*
+ * the name of tagged pointer types are tagptr{1, 2, 3...}_t
+ * avoid directly using the internal structs __tagptr{1, 2, 3...}
+ */
+#define __MAKE_TAGPTR(n) \
+typedef struct __tagptr##n {	\
+	uintptr_t v;	\
+} tagptr##n##_t;
+
+__MAKE_TAGPTR(1)
+__MAKE_TAGPTR(2)
+__MAKE_TAGPTR(3)
+__MAKE_TAGPTR(4)
+
+#undef __MAKE_TAGPTR
+
+extern void __compiletime_error("bad tagptr tags")
+	__bad_tagptr_tags(void);
+
+extern void __compiletime_error("bad tagptr type")
+	__bad_tagptr_type(void);
+
+/* fix the broken usage of "#define tagptr2_t tagptr3_t" by users */
+#define __tagptr_mask_1(ptr, n)	\
+	__builtin_types_compatible_p(typeof(ptr), struct __tagptr##n) ? \
+		(1UL << (n)) - 1 :
+
+#define __tagptr_mask(ptr)	(\
+	__tagptr_mask_1(ptr, 1) ( \
+	__tagptr_mask_1(ptr, 2) ( \
+	__tagptr_mask_1(ptr, 3) ( \
+	__tagptr_mask_1(ptr, 4) ( \
+	__bad_tagptr_type(), 0)))))
+
+/* generate a tagged pointer from a raw value */
+#define tagptr_init(type, val) \
+	((typeof(type)){ .v = (uintptr_t)(val) })
+
+/*
+ * directly cast a tagged pointer to the native pointer type, which
+ * could be used for backward compatibility of existing code.
+ */
+#define tagptr_cast_ptr(tptr) ((void *)(tptr).v)
+
+/* encode tagged pointers */
+#define tagptr_fold(type, ptr, _tags) ({ \
+	const typeof(_tags) tags = (_tags); \
+	if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(type))) \
+		__bad_tagptr_tags(); \
+tagptr_init(type, (uintptr_t)(ptr) | tags); })
+
+/* decode tagged pointers */
+#define tagptr_unfold_ptr(tptr) \
+	((void *)((tptr).v & ~__tagptr_mask(tptr)))
+
+#define tagptr_unfold_tags(tptr) \
+	((tptr).v & __tagptr_mask(tptr))
+
+/* operations for the tagger pointer */
+#define tagptr_eq(_tptr1, _tptr2) ({ \
+	typeof(_tptr1) tptr1 = (_tptr1); \
+	typeof(_tptr2) tptr2 = (_tptr2); \
+	(void) (&tptr1 == &tptr2); \
+(tptr1).v == (tptr2).v; })
+
+/* lock-free CAS operation */
+#define tagptr_cmpxchg(_ptptr, _o, _n) ({ \
+	typeof(_ptptr) ptptr = (_ptptr); \
+	typeof(_o) o = (_o); \
+	typeof(_n) n = (_n); \
+	(void) (&o == &n); \
+	(void) (&o == ptptr); \
+tagptr_init(o, cmpxchg(&ptptr->v, o.v, n.v)); })
+
+/* wrap WRITE_ONCE if atomic update is needed */
+#define tagptr_replace_tags(_ptptr, tags) ({ \
+	typeof(_ptptr) ptptr = (_ptptr); \
+	*ptptr = tagptr_fold(*ptptr, tagptr_unfold_ptr(*ptptr), tags); \
+*ptptr; })
+
+#define tagptr_set_tags(_ptptr, _tags) ({ \
+	typeof(_ptptr) ptptr = (_ptptr); \
+	const typeof(_tags) tags = (_tags); \
+	if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(*ptptr))) \
+		__bad_tagptr_tags(); \
+	ptptr->v |= tags; \
+*ptptr; })
+
+#define tagptr_clear_tags(_ptptr, _tags) ({ \
+	typeof(_ptptr) ptptr = (_ptptr); \
+	const typeof(_tags) tags = (_tags); \
+	if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(*ptptr))) \
+		__bad_tagptr_tags(); \
+	ptptr->v &= ~tags; \
+*ptptr; })
+
+#endif
+
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-06-30  9:47 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-28  9:06 [RFC PATCH] <linux/taggedptr.h>: Introduce tagged pointer Gao Xiang
2018-06-28  9:23 ` Matthew Wilcox
2018-06-28  9:40   ` Gao Xiang
2018-06-28 14:17 ` [RFC PATCH v2] <linux/tagptr.h>: " Gao Xiang
2018-06-28 15:02 ` [RFC PATCH RESEND v3] " Gao Xiang
2018-06-30  9:47 ` [RFC PATCH v4] " Gao Xiang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).