diff mbox series

[-next,v3,1/3] lib/string_choices: Add str_true_false()/str_false_true() helper

Message ID 20240827024517.914100-2-lihongbo22@huawei.com (mailing list archive)
State New
Headers show
Series Add str_true_false()/str_false_true() helper | expand

Commit Message

Hongbo Li Aug. 27, 2024, 2:45 a.m. UTC
Add str_true_false()/str_false_true() helper to return "true" or
"false" string literal.

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
---
 include/linux/string_choices.h | 6 ++++++
 1 file changed, 6 insertions(+)

Comments

Andrew Morton Aug. 27, 2024, 11:42 p.m. UTC | #1
On Tue, 27 Aug 2024 10:45:15 +0800 Hongbo Li <lihongbo22@huawei.com> wrote:

> Add str_true_false()/str_false_true() helper to return "true" or
> "false" string literal.
> 
> ...
>
> --- a/include/linux/string_choices.h
> +++ b/include/linux/string_choices.h
> @@ -48,6 +48,12 @@ static inline const char *str_up_down(bool v)
>  }
>  #define str_down_up(v)		str_up_down(!(v))
>  
> +static inline const char *str_true_false(bool v)
> +{
> +	return v ? "true" : "false";
> +}
> +#define str_false_true(v)		str_true_false(!(v))
> +
>  /**
>   * str_plural - Return the simple pluralization based on English counts
>   * @num: Number used for deciding pluralization

This might result in copies of the strings "true" and "false" being
generated for every .c file which uses this function, resulting in
unnecessary bloat.

It's possible that the compiler/linker can eliminate this duplication. 
If not, I suggest that every function in string_choices.h be uninlined.
Hongbo Li Aug. 28, 2024, 1:48 a.m. UTC | #2
On 2024/8/28 7:42, Andrew Morton wrote:
> On Tue, 27 Aug 2024 10:45:15 +0800 Hongbo Li <lihongbo22@huawei.com> wrote:
> 
>> Add str_true_false()/str_false_true() helper to return "true" or
>> "false" string literal.
>>
>> ...
>>
>> --- a/include/linux/string_choices.h
>> +++ b/include/linux/string_choices.h
>> @@ -48,6 +48,12 @@ static inline const char *str_up_down(bool v)
>>   }
>>   #define str_down_up(v)		str_up_down(!(v))
>>   
>> +static inline const char *str_true_false(bool v)
>> +{
>> +	return v ? "true" : "false";
>> +}
>> +#define str_false_true(v)		str_true_false(!(v))
>> +
>>   /**
>>    * str_plural - Return the simple pluralization based on English counts
>>    * @num: Number used for deciding pluralization
> 
> This might result in copies of the strings "true" and "false" being
> generated for every .c file which uses this function, resulting in
> unnecessary bloat.
> 
> It's possible that the compiler/linker can eliminate this duplication.
> If not, I suggest that every function in string_choices.h be uninlined.
The inline function is in header file, it will cause code expansion. It 
should avoid the the copies of the strings.

Thanks,
Hongbo
Andrew Morton Aug. 28, 2024, 3:22 a.m. UTC | #3
On Wed, 28 Aug 2024 09:48:21 +0800 Hongbo Li <lihongbo22@huawei.com> wrote:

> > This might result in copies of the strings "true" and "false" being
> > generated for every .c file which uses this function, resulting in
> > unnecessary bloat.
> > 
> > It's possible that the compiler/linker can eliminate this duplication.
> > If not, I suggest that every function in string_choices.h be uninlined.
> The inline function is in header file, it will cause code expansion. It 
> should avoid the the copies of the strings.

Sorry, I don't understand your reply.

Anything which is calling these functions is not performance-sensitive,
so optimizing for space is preferred.  An out-of-line function which
returns a const char * will achieve this?
Hongbo Li Aug. 28, 2024, 3:49 a.m. UTC | #4
On 2024/8/28 11:22, Andrew Morton wrote:
> On Wed, 28 Aug 2024 09:48:21 +0800 Hongbo Li <lihongbo22@huawei.com> wrote:
> 
>>> This might result in copies of the strings "true" and "false" being
>>> generated for every .c file which uses this function, resulting in
>>> unnecessary bloat.
>>>
>>> It's possible that the compiler/linker can eliminate this duplication.
>>> If not, I suggest that every function in string_choices.h be uninlined.
>> The inline function is in header file, it will cause code expansion. It
>> should avoid the the copies of the strings.
> 
> Sorry, I don't understand your reply.
> 
I mean this is a inline function (and tiny enough), the compiler will do 
the code expansion and some optimizations.
> Anything which is calling these functions is not performance-sensitive,
> so optimizing for space is preferred.  An out-of-line function which
> returns a const char * will achieve this?
I think this helper can achieve this. Because it is tiny enough, the 
compiler will handle this like #define macro (do the replacement) 
without allocating extra functional stack. On the contrary, if it is 
implemented as a non-inline function, it will cause extra functional 
stack when it was called every time. And it also should be implemented 
in a source file (.c file), not in header file(.h file).

Thanks,
Hongbo
Andy Shevchenko Aug. 28, 2024, 12:59 p.m. UTC | #5
On Tue, Aug 27, 2024 at 04:42:18PM -0700, Andrew Morton wrote:
> On Tue, 27 Aug 2024 10:45:15 +0800 Hongbo Li <lihongbo22@huawei.com> wrote:

...

> > +#define str_false_true(v)		str_true_false(!(v))

> This might result in copies of the strings "true" and "false" being
> generated for every .c file which uses this function, resulting in
> unnecessary bloat.
> 
> It's possible that the compiler/linker can eliminate this duplication. 
> If not, I suggest that every function in string_choices.h be uninlined.

From this perspective this patch doesn't change anything.
The function is inline and in the same compilation module the linker will
optimise away the duplicates (note as well that this is kinda new feature,
some relatively old GCC might not have this feature, but I'm not an expert
in the area).
Andrew Morton Aug. 28, 2024, 8:25 p.m. UTC | #6
On Wed, 28 Aug 2024 11:49:51 +0800 Hongbo Li <lihongbo22@huawei.com> wrote:

> > Anything which is calling these functions is not performance-sensitive,
> > so optimizing for space is preferred.  An out-of-line function which
> > returns a const char * will achieve this?
> I think this helper can achieve this. Because it is tiny enough, the 
> compiler will handle this like #define macro (do the replacement) 
> without allocating extra functional stack. On the contrary, if it is 
> implemented as a non-inline function, it will cause extra functional 
> stack when it was called every time. And it also should be implemented 
> in a source file (.c file), not in header file(.h file).

No, my concern is that if, for example, str_high_low() gets used in 100
.c files then we get 100 copies of the strings "high" and "low" in
vmlinux.  Making str_high_low() uninlined would fix this.

However a quick experiment tells me that the compiler and linker are
indeed able to perform this cross-object-file optimization:

--- a/fs/open.c~a
+++ a/fs/open.c
@@ -34,6 +34,8 @@
 #include <linux/mnt_idmapping.h>
 #include <linux/filelock.h>
 
+#include <linux/string_choices.h>
+
 #include "internal.h"
 
 int do_truncate(struct mnt_idmap *idmap, struct dentry *dentry,
@@ -42,6 +44,8 @@ int do_truncate(struct mnt_idmap *idmap,
 	int ret;
 	struct iattr newattrs;
 
+	printk("%s\n", frozzle(dentry == NULL));
+
 	/* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */
 	if (length < 0)
 		return -EINVAL;
--- a/fs/inode.c~a
+++ a/fs/inode.c
@@ -22,6 +22,9 @@
 #include <linux/iversion.h>
 #include <linux/rw_hint.h>
 #include <trace/events/writeback.h>
+
+#include <linux/string_helpers.h>
+
 #include "internal.h"
 
 /*
@@ -110,6 +113,8 @@ static struct inodes_stat_t inodes_stat;
 static int proc_nr_inodes(const struct ctl_table *table, int write, void *buffer,
 			  size_t *lenp, loff_t *ppos)
 {
+	printk("%s\n", frozzle(table == NULL));
+
 	inodes_stat.nr_inodes = get_nr_inodes();
 	inodes_stat.nr_unused = get_nr_inodes_unused();
 	return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
--- a/include/linux/string_choices.h~a
+++ a/include/linux/string_choices.h
@@ -4,6 +4,11 @@
 
 #include <linux/types.h>
 
+static inline const char *frozzle(bool v)
+{
+	return v ? "frizzle" : "frazzle";
+}
+
 static inline const char *str_enable_disable(bool v)
 {
 	return v ? "enable" : "disable";
Hongbo Li Aug. 29, 2024, 1:12 a.m. UTC | #7
On 2024/8/29 4:25, Andrew Morton wrote:
> On Wed, 28 Aug 2024 11:49:51 +0800 Hongbo Li <lihongbo22@huawei.com> wrote:
> 
>>> Anything which is calling these functions is not performance-sensitive,
>>> so optimizing for space is preferred.  An out-of-line function which
>>> returns a const char * will achieve this?
>> I think this helper can achieve this. Because it is tiny enough, the
>> compiler will handle this like #define macro (do the replacement)
>> without allocating extra functional stack. On the contrary, if it is
>> implemented as a non-inline function, it will cause extra functional
>> stack when it was called every time. And it also should be implemented
>> in a source file (.c file), not in header file(.h file).
> 
> No, my concern is that if, for example, str_high_low() gets used in 100
> .c files then we get 100 copies of the strings "high" and "low" in
> vmlinux.  Making str_high_low() uninlined would fix this.

At first, I didn't consider these aspects.

> 
> However a quick experiment tells me that the compiler and linker are
> indeed able to perform this cross-object-file optimization:
> 
That's very good!

Thanks,
Hongbo

> --- a/fs/open.c~a
> +++ a/fs/open.c
> @@ -34,6 +34,8 @@
>   #include <linux/mnt_idmapping.h>
>   #include <linux/filelock.h>
>   
> +#include <linux/string_choices.h>
> +
>   #include "internal.h"
>   
>   int do_truncate(struct mnt_idmap *idmap, struct dentry *dentry,
> @@ -42,6 +44,8 @@ int do_truncate(struct mnt_idmap *idmap,
>   	int ret;
>   	struct iattr newattrs;
>   
> +	printk("%s\n", frozzle(dentry == NULL));
> +
>   	/* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */
>   	if (length < 0)
>   		return -EINVAL;
> --- a/fs/inode.c~a
> +++ a/fs/inode.c
> @@ -22,6 +22,9 @@
>   #include <linux/iversion.h>
>   #include <linux/rw_hint.h>
>   #include <trace/events/writeback.h>
> +
> +#include <linux/string_helpers.h>
> +
>   #include "internal.h"
>   
>   /*
> @@ -110,6 +113,8 @@ static struct inodes_stat_t inodes_stat;
>   static int proc_nr_inodes(const struct ctl_table *table, int write, void *buffer,
>   			  size_t *lenp, loff_t *ppos)
>   {
> +	printk("%s\n", frozzle(table == NULL));
> +
>   	inodes_stat.nr_inodes = get_nr_inodes();
>   	inodes_stat.nr_unused = get_nr_inodes_unused();
>   	return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
> --- a/include/linux/string_choices.h~a
> +++ a/include/linux/string_choices.h
> @@ -4,6 +4,11 @@
>   
>   #include <linux/types.h>
>   
> +static inline const char *frozzle(bool v)
> +{
> +	return v ? "frizzle" : "frazzle";
> +}
> +
>   static inline const char *str_enable_disable(bool v)
>   {
>   	return v ? "enable" : "disable";
> _
> 
> 
> x1:/usr/src/25> strings vmlinux|grep frazzle
> frazzle
> x1:/usr/src/25>
> 
> See, only one copy!
>
Kees Cook Sept. 5, 2024, 4:51 p.m. UTC | #8
On Wed, 28 Aug 2024 13:25:09 -0700, Andrew Morton wrote:
> On Wed, 28 Aug 2024 11:49:51 +0800 Hongbo Li <lihongbo22@huawei.com> wrote:
> 
> > > Anything which is calling these functions is not performance-sensitive,
> > > so optimizing for space is preferred.  An out-of-line function which
> > > returns a const char * will achieve this?
> > I think this helper can achieve this. Because it is tiny enough, the
> > compiler will handle this like #define macro (do the replacement)
> > without allocating extra functional stack. On the contrary, if it is
> > implemented as a non-inline function, it will cause extra functional
> > stack when it was called every time. And it also should be implemented
> > in a source file (.c file), not in header file(.h file).
> 
> [...]

Since I've taken over string maintainership, I've applied this to my
tree (where other similar changes are appearing). This should reduce
conflicts here...

Applied to for-next/hardening, thanks!

[1/3] lib/string_choices: Add str_true_false()/str_false_true() helper
      https://git.kernel.org/kees/c/6ff4cd1160af

Take care,
diff mbox series

Patch

diff --git a/include/linux/string_choices.h b/include/linux/string_choices.h
index 1320bcdcb89c..ebcc56b28ede 100644
--- a/include/linux/string_choices.h
+++ b/include/linux/string_choices.h
@@ -48,6 +48,12 @@  static inline const char *str_up_down(bool v)
 }
 #define str_down_up(v)		str_up_down(!(v))
 
+static inline const char *str_true_false(bool v)
+{
+	return v ? "true" : "false";
+}
+#define str_false_true(v)		str_true_false(!(v))
+
 /**
  * str_plural - Return the simple pluralization based on English counts
  * @num: Number used for deciding pluralization