[PATCH i-g-t v2 2/2] lib/igt_sysfs: make sure to write empty strings
Lucas De Marchi
lucas.demarchi at intel.com
Tue Feb 27 05:26:50 UTC 2024
On Mon, Feb 26, 2024 at 10:11:05PM +0100, Janusz Krzysztofik wrote:
>Hi Lucas,
>
>On Thursday, 22 February 2024 20:33:26 CET Lucas De Marchi wrote:
>> echo -n "" > /sys/module/<modulename>/parameters/<param>
>>
>> doesn't really work as it will just create a open() + close() expecting
>> the file to be truncated. The same issue happens with igt as it will
>> stop writing when there are 0 chars to write. Special case the empty
>> string so it always write a '\0' and make sure igt_sysfs_set() accounts
>> for the extra null char.
>>
>> Shell example:
>> # echo -n "/foo" > /sys/module/firmware_class/parameters/path
>> # cat /sys/module/firmware_class/parameters/path
>> /foo
>> # echo -n "" > /sys/module/firmware_class/parameters/path
>> /foo
>> # # make sure to actually write a \0:
>> echo -ne "\0" > /sys/module/firmware_class/parameters/path
>> # cat /sys/module/firmware_class/parameters/path
>>
>> Same thing happens when testing igt_sysfs_set():
>> int dirfd = open("/sys/module/firmware_class/parameters", O_RDONLY);
>> igt_sysfs_set(dirfd, "path", "");
>>
>> Previously it was not really setting the param.
>>
>> v2:
>> - Fix return code from igt_sysfs_vprintf() to differentiate between
>> writing 1 or 0 chars (Janusz)
>> - Document the behavior of igt_sysfs_set() as being a higher-level
>> than igt_sysfs_write().
>>
>> Signed-off-by: Lucas De Marchi <lucas.demarchi at intel.com>
>> ---
>> lib/igt_sysfs.c | 28 +++++++++++++++++++++++-----
>> 1 file changed, 23 insertions(+), 5 deletions(-)
>>
>> diff --git a/lib/igt_sysfs.c b/lib/igt_sysfs.c
>> index 2b0225138..d5d5b249f 100644
>> --- a/lib/igt_sysfs.c
>> +++ b/lib/igt_sysfs.c
>> @@ -349,6 +349,9 @@ int igt_sysfs_get_num_gt(int device)
>> * @len: the length to write
>> *
>> * This writes @len bytes from @data to the sysfs file.
>> + * Contrary to igt_sysfs_set(), this does not automatically add a
>> + * null char to terminate the data. It's caller responsibility to pass
>> + * the right len according to the data being written.
>> *
>> * Returns:
>> * The number of bytes written, or -errno on error.
>> @@ -406,7 +409,12 @@ int igt_sysfs_read(int dir, const char *attr, void *data, int len)
>> */
>> bool igt_sysfs_set(int dir, const char *attr, const char *value)
>> {
>> - int len = strlen(value);
>> + /*
>> + * Always write the null char at the end, to guarantee the write goes
>> + * through, even for an empty string.
>> + */
>> + int len = strlen(value) + 1;
>
>Since pre-merge results indicate some sysfs entries don't like that extra null
>char, I'm assuming you are going to submit a fixed version.
yes, basically I have to return back to my previous version, changing
the behavior only when it's an empty string. But this time I tweaked the
return code so we don't return 1 when writting an empty string.
That's because some places in the kernel try to play nice with a
possible \n in the end of the string and do a strchr(s, '\n'). If it
doesn't find, then it considers the passed size to be the len and then
compare with possible values. However now the lengths don't match and it
fails. Example: pm_test. See kernel/power/main.c:pm_test_store on the
kernel side.
I thought about a quick fix on the kernel side that would basically be:
- len = p ? p - buf : n;
+ len = p ? p - buf : strnlen(buf, n);
but it would be hard to know how many other places use a similar pattern
and would start failing.
Lucas De Marchi
>
>> +
>> return igt_sysfs_write(dir, attr, value, len) == len;
>> }
>>
>> @@ -506,7 +514,7 @@ int igt_sysfs_vprintf(int dir, const char *attr, const char *fmt, va_list ap)
>> {
>> char stack[128], *buf = stack;
>> va_list tmp;
>> - int ret, fd;
>> + int ret, fd, len;
>>
>> fd = openat(dir, attr, O_WRONLY);
>> if (igt_debug_on(fd < 0))
>> @@ -515,11 +523,12 @@ int igt_sysfs_vprintf(int dir, const char *attr, const char *fmt, va_list ap)
>> va_copy(tmp, ap);
>> ret = vsnprintf(stack, sizeof(stack), fmt, tmp);
>> va_end(tmp);
>> +
>
>NIT: Not related.
>
>> if (igt_debug_on(ret < 0))
>> return -EINVAL;
>>
>> if (ret > sizeof(stack)) {
>> - unsigned int len = ret + 1;
>> + len = ret + 1;
>>
>> buf = malloc(len);
>> if (igt_debug_on(!buf))
>> @@ -532,13 +541,22 @@ int igt_sysfs_vprintf(int dir, const char *attr, const char *fmt, va_list ap)
>> }
>> }
>>
>> - ret = igt_writen(fd, buf, ret);
>> + /*
>> + * Make sure to always issue a write() syscall, even if writing an
>> + * empty string, otherwise values in sysfs like module parameters don't
>> + * really get overwritten. vsnprintf() guarantees to return a \0
>> + * terminated string, so just add that char. The return code is still
>> + * the same as before, to abstract that from caller.
>> + */
>> + len = ret + 1;
>
>Potentially the same issue with some sysfs entries, I believe.
>
>Thanks,
>Janusz
>
>
>> +
>> + ret = igt_writen(fd, buf, len);
>>
>> close(fd);
>> if (buf != stack)
>> free(buf);
>>
>> - return ret;
>> + return ret == len ? len - 1 : ret;
>> }
>>
>> /**
>>
>
>
>
>
More information about the igt-dev
mailing list