2024-02-05 10:11:48

by Alexey Dobriyan

[permalink] [raw]
Subject: [PATCH] sysfs: make sysfs_emit() return ssize_t

sysfs_emit() is most often found in functions returning ssize_t
not int:

static ssize_t oops_count_show(...)
{
return sysfs_emit(page, ...);
}

This pattern results in sign-extension instruction between
sysfs_emit() return value (int) and caller return value (which is
ssize_t).

But it is better to do sign-extension once inside sysfs_emit()
then duplicate it at nearly every call site on 64-bit.

Signed-off-by: Alexey Dobriyan <[email protected]>
---

fs/sysfs/file.c | 2 +-
include/linux/sysfs.h | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)

--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -739,7 +739,7 @@ EXPORT_SYMBOL_GPL(sysfs_change_owner);
*
* Returns number of characters written to @buf.
*/
-int sysfs_emit(char *buf, const char *fmt, ...)
+ssize_t sysfs_emit(char *buf, const char *fmt, ...)
{
va_list args;
int len;
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -356,7 +356,7 @@ int sysfs_group_change_owner(struct kobject *kobj,
const struct attribute_group *groups, kuid_t kuid,
kgid_t kgid);
__printf(2, 3)
-int sysfs_emit(char *buf, const char *fmt, ...);
+ssize_t sysfs_emit(char *buf, const char *fmt, ...);
__printf(3, 4)
int sysfs_emit_at(char *buf, int at, const char *fmt, ...);

@@ -607,7 +607,7 @@ static inline int sysfs_group_change_owner(struct kobject *kobj,
}

__printf(2, 3)
-static inline int sysfs_emit(char *buf, const char *fmt, ...)
+static inline ssize_t sysfs_emit(char *buf, const char *fmt, ...)
{
return 0;
}


2024-03-07 22:09:46

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH] sysfs: make sysfs_emit() return ssize_t

On Mon, Feb 05, 2024 at 01:11:36PM +0300, Alexey Dobriyan wrote:
> sysfs_emit() is most often found in functions returning ssize_t
> not int:
>
> static ssize_t oops_count_show(...)
> {
> return sysfs_emit(page, ...);
> }
>
> This pattern results in sign-extension instruction between
> sysfs_emit() return value (int) and caller return value (which is
> ssize_t).

Is that a problem?

> But it is better to do sign-extension once inside sysfs_emit()
> then duplicate it at nearly every call site on 64-bit.

Why is that better? Does this affect code generation? If so, how much?
And to what affect?

And the function itself really is dealing with an int, it's up to the
caller to want to do something with that, not the sysfs_emit() call
itself.

thanks,

greg k-h

2024-03-08 06:26:15

by Alexey Dobriyan

[permalink] [raw]
Subject: Re: [PATCH] sysfs: make sysfs_emit() return ssize_t

On Thu, Mar 07, 2024 at 10:04:41PM +0000, Greg Kroah-Hartman wrote:
> On Mon, Feb 05, 2024 at 01:11:36PM +0300, Alexey Dobriyan wrote:
> > sysfs_emit() is most often found in functions returning ssize_t
> > not int:
> >
> > static ssize_t oops_count_show(...)
> > {
> > return sysfs_emit(page, ...);
> > }
> >
> > This pattern results in sign-extension instruction between
> > sysfs_emit() return value (int) and caller return value (which is
> > ssize_t).
>
> Is that a problem?

Small problem, but, yes.

If sysfs_emit() returns "int", then every user compiles to

call sysfs_emit
movsx rax, eax
ret

Given than sysfs_emit() is the official way to print in sysfs,
there are lots of users and there will be more users in the future
as it grows.

This trailing "movsx" instruction is duplicated every time.

If sysfs_emit() returns ssize_t then it is folded into sysfs_emit() and
appears in the code _once_ saving bytes.

Ultimately, all this confusion and mismatches come from snprintf()
accepting "size_t" but returning "int" (despite working on very large
strings!) which sysfs_emit() copied.

> > But it is better to do sign-extension once inside sysfs_emit()
> > then duplicate it at nearly every call site on 64-bit.
>
> Why is that better? Does this affect code generation? If so, how much?
> And to what affect?
>
> And the function itself really is dealing with an int, it's up to the
> caller to want to do something with that, not the sysfs_emit() call
> itself.

2024-03-08 09:01:41

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH] sysfs: make sysfs_emit() return ssize_t

On Fri, Mar 08, 2024 at 09:26:01AM +0300, Alexey Dobriyan wrote:
> On Thu, Mar 07, 2024 at 10:04:41PM +0000, Greg Kroah-Hartman wrote:
> > On Mon, Feb 05, 2024 at 01:11:36PM +0300, Alexey Dobriyan wrote:
> > > sysfs_emit() is most often found in functions returning ssize_t
> > > not int:
> > >
> > > static ssize_t oops_count_show(...)
> > > {
> > > return sysfs_emit(page, ...);
> > > }
> > >
> > > This pattern results in sign-extension instruction between
> > > sysfs_emit() return value (int) and caller return value (which is
> > > ssize_t).
> >
> > Is that a problem?
>
> Small problem, but, yes.
>
> If sysfs_emit() returns "int", then every user compiles to
>
> call sysfs_emit
> movsx rax, eax
> ret
>
> Given than sysfs_emit() is the official way to print in sysfs,
> there are lots of users and there will be more users in the future
> as it grows.
>
> This trailing "movsx" instruction is duplicated every time.
>
> If sysfs_emit() returns ssize_t then it is folded into sysfs_emit() and
> appears in the code _once_ saving bytes.
>
> Ultimately, all this confusion and mismatches come from snprintf()
> accepting "size_t" but returning "int" (despite working on very large
> strings!) which sysfs_emit() copied.

True, then why not fix up the base function here, vscnprintf() and
vsnprintf() and then propagate the fixes outward?

thanks,

greg k-h

2024-03-10 19:21:36

by David Laight

[permalink] [raw]
Subject: RE: [PATCH] sysfs: make sysfs_emit() return ssize_t

From: Alexey Dobriyan
> Sent: 08 March 2024 06:26
>
> On Thu, Mar 07, 2024 at 10:04:41PM +0000, Greg Kroah-Hartman wrote:
> > On Mon, Feb 05, 2024 at 01:11:36PM +0300, Alexey Dobriyan wrote:
> > > sysfs_emit() is most often found in functions returning ssize_t
> > > not int:
> > >
> > > static ssize_t oops_count_show(...)
> > > {
> > > return sysfs_emit(page, ...);
> > > }
> > >
> > > This pattern results in sign-extension instruction between
> > > sysfs_emit() return value (int) and caller return value (which is
> > > ssize_t).
> >
> > Is that a problem?
>
> Small problem, but, yes.
>
> If sysfs_emit() returns "int", then every user compiles to
>
> call sysfs_emit
> movsx rax, eax
> ret
>
> Given than sysfs_emit() is the official way to print in sysfs,
> there are lots of users and there will be more users in the future
> as it grows.
>
> This trailing "movsx" instruction is duplicated every time.

Actually you are missing a tail call...
But no one will notice the extra execution time, it will
be masked by the cost of the formatting.
So it is a small amount of I-cache.

In reality the best fix is not to use ssize_t for something
where the domain of the value is much smaller.
In this case it is probably unsigned as well.

Efficient calling conventions for 64bit code probably require
all non-negative types be passed/returned as 'unsigned long' and
then copied to an 'unsigned int' to get better code generation.
Signed values are much more difficult though.
But unless you are trying to get that last extra clock there
are likely to be much lower hanging fruit.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

2024-03-11 04:55:43

by Alexey Dobriyan

[permalink] [raw]
Subject: Re: [PATCH] sysfs: make sysfs_emit() return ssize_t

On Sun, Mar 10, 2024 at 07:21:32PM +0000, David Laight wrote:
> From: Alexey Dobriyan
> > Sent: 08 March 2024 06:26
> >
> > On Thu, Mar 07, 2024 at 10:04:41PM +0000, Greg Kroah-Hartman wrote:
> > > On Mon, Feb 05, 2024 at 01:11:36PM +0300, Alexey Dobriyan wrote:
> > > > sysfs_emit() is most often found in functions returning ssize_t
> > > > not int:
> > > >
> > > > static ssize_t oops_count_show(...)
> > > > {
> > > > return sysfs_emit(page, ...);
> > > > }
> > > >
> > > > This pattern results in sign-extension instruction between
> > > > sysfs_emit() return value (int) and caller return value (which is
> > > > ssize_t).
> > >
> > > Is that a problem?
> >
> > Small problem, but, yes.
> >
> > If sysfs_emit() returns "int", then every user compiles to
> >
> > call sysfs_emit
> > movsx rax, eax
> > ret
> >
> > Given than sysfs_emit() is the official way to print in sysfs,
> > there are lots of users and there will be more users in the future
> > as it grows.
> >
> > This trailing "movsx" instruction is duplicated every time.
>
> Actually you are missing a tail call...
> But no one will notice the extra execution time, it will
> be masked by the cost of the formatting.
> So it is a small amount of I-cache.
>
> In reality the best fix is not to use ssize_t for something
> where the domain of the value is much smaller.
> In this case it is probably unsigned as well.

It is much harder to switch read hooks from returning ssize_t to int.
Kernel snprintf() too.

sysfs_emit() change will be mostly seamless, but function pointers
generate warnings.