* Re: [PATCH] docs: process: fix broken link
From: Jonathan Corbet @ 2019-08-24 21:18 UTC (permalink / raw)
To: Jacob Huisman; +Cc: Federico Vaga, Harry Wei, Alex Shi, linux-doc, linux-kernel
In-Reply-To: <20190816122209.5bz4rlln5cahn7ki@jacob-MS-7A62>
On Fri, 16 Aug 2019 13:22:09 +0100
Jacob Huisman <jacobhuisman@kernelthusiast.com> wrote:
> http://linux.yyz.us/patch-format.html seems to be down since
> approximately September 2018. There is a working archive copy on
> arhive.org. Replaced the links in documenation + translations.
>
> Signed-off-by: Jacob Huisman <jacobhuisman@kernelthusiast.com>
> ---
> Documentation/process/howto.rst | 2 +-
> Documentation/process/submitting-patches.rst | 2 +-
> Documentation/translations/it_IT/process/howto.rst | 2 +-
> Documentation/translations/it_IT/process/submitting-patches.rst | 2 +-
> Documentation/translations/ja_JP/SubmittingPatches | 2 +-
> Documentation/translations/ja_JP/howto.rst | 2 +-
> Documentation/translations/ko_KR/howto.rst | 2 +-
> Documentation/translations/zh_CN/process/howto.rst | 2 +-
> Documentation/translations/zh_CN/process/submitting-patches.rst | 2 +-
> 9 files changed, 9 insertions(+), 9 deletions(-)
Dead links don't help anybody, so I've applied this. I have to wonder,
though, whether there is really value in carrying around a link to a page
that hasn't been maintained in years. Almost everything that appears
there is already in submitting-patches.rst; maybe we could see if there's
anything that's missing, add it, then drop the link?
Thanks,
jon
^ permalink raw reply
* Re: [PATCH v2] Ext4 documentation fixes.
From: Jonathan Corbet @ 2019-08-24 21:24 UTC (permalink / raw)
To: Andreas Dilger
Cc: Ayush Ranjan, Theodore Ts'o, Andreas Dilger,
Ext4 Developers List, linux-doc, Linux Kernel Mailing List
In-Reply-To: <DEDD6BA5-6E18-4ED6-9EF6-E11EDA593700@dilger.ca>
On Thu, 15 Aug 2019 09:16:23 -0700
Andreas Dilger <adilger@dilger.ca> wrote:
> On Aug 15, 2019, at 09:11, Ayush Ranjan <ayushr2@illinois.edu> wrote:
> >
> > This commit aims to fix the following issues in ext4 documentation:
> > - Flexible block group docs said that the aim was to group block
> > metadata together instead of block group metadata.
> > - The documentation consistly uses "location" instead of "block number".
> > It is easy to confuse location to be an absolute offset on disk. Added
> > a line to clarify all location values are in terms of block numbers.
> > - Dirent2 docs said that the rec_len field is shortened instead of the
> > name_len field.
> > - Typo in bg_checksum description.
> > - Inode size is 160 bytes now, and hence i_extra_isize is now 32.
> > - Cluster size formula was incorrect, it did not include the +10 to
> > s_log_cluster_size value.
> > - Typo: there were two s_wtime_hi in the superblock struct.
> > - Superblock struct was outdated, added the new fields which were part
> > of s_reserved earlier.
> > - Multiple mount protection seems to be implemented in fs/ext4/mmp.c.
> >
> > Signed-off-by: Ayush Ranjan <ayushr2@illinois.edu>
>
> Reviewed-by: Andreas Dilger <adilger@dilger.ca>
I've applied this to the docs tree.
However, Ayush: the patch was rather badly corrupted by your mail client.
I managed to fix it up, but please in the future verify that you can email
a patch to yourself and apply it before submitting it. There may be some
useful hints in Documentation/process/email-clients.rst .
Thanks,
jon
^ permalink raw reply
* Re: [tip: x86/urgent] x86/CPU/AMD: Clear RDRAND CPUID bit on AMD family 15h/16h
From: H. Peter Anvin @ 2019-08-24 21:35 UTC (permalink / raw)
To: Pavel Machek, linux-kernel
Cc: linux-tip-commits, x86@kernel.org, Thomas Gleixner, stable,
Rafael J. Wysocki, Paolo Bonzini, Nathan Chancellor,
linux-pm@vger.kernel.org, linux-doc@vger.kernel.org, Kees Cook,
Juergen Gross, Josh Poimboeuf, Jonathan Corbet, Ingo Molnar,
Chen Yu, Andrew Morton, Andrew Cooper, Borislav Petkov,
Tom Lendacky
In-Reply-To: <20190824181929.GA18551@amd>
On 8/24/19 11:19 AM, Pavel Machek wrote:
> On Fri 2019-08-23 01:10:49, tip-bot2 for Tom Lendacky wrote:
>> The following commit has been merged into the x86/urgent branch of tip:
>>
>> Commit-ID: c49a0a80137c7ca7d6ced4c812c9e07a949f6f24
>> Gitweb: https://git.kernel.org/tip/c49a0a80137c7ca7d6ced4c812c9e07a949f6f24
>> Author: Tom Lendacky <thomas.lendacky@amd.com>
>> AuthorDate: Mon, 19 Aug 2019 15:52:35
>> Committer: Borislav Petkov <bp@suse.de>
>> CommitterDate: Mon, 19 Aug 2019 19:42:52 +02:00
>>
>> x86/CPU/AMD: Clear RDRAND CPUID bit on AMD family 15h/16h
>>
>> There have been reports of RDRAND issues after resuming from suspend on
>> some AMD family 15h and family 16h systems. This issue stems from a BIOS
>> not performing the proper steps during resume to ensure RDRAND continues
>> to function properly.
>
> There are quite a few unanswered questions here.
>
> a) Is there/should there be CVE for this?
>
> b) Can we perform proper steps in kernel, thus making RDRAND usable
> even when BIOS is buggy?
>
The kernel should at least be able to set its internal "CPUID" bit, visible
through /proc/cpuinfo.
-hpa
^ permalink raw reply
* Re: [PATCH v2] Ext4 documentation fixes.
From: Theodore Y. Ts'o @ 2019-08-24 23:09 UTC (permalink / raw)
To: Jonathan Corbet
Cc: Andreas Dilger, Ayush Ranjan, Andreas Dilger,
Ext4 Developers List, linux-doc, Linux Kernel Mailing List
In-Reply-To: <20190824152453.03737143@lwn.net>
On Sat, Aug 24, 2019 at 03:24:53PM -0600, Jonathan Corbet wrote:
> I've applied this to the docs tree.
>
> However, Ayush: the patch was rather badly corrupted by your mail client.
> I managed to fix it up, but please in the future verify that you can email
> a patch to yourself and apply it before submitting it. There may be some
> useful hints in Documentation/process/email-clients.rst .
Hi Jon,
If you haven't pushed out your doc tree, can you please drop it? I've
already applied an (improved) version of this patch to the ext4 tree,
and I actually have some plans to do further fixups of ext4 on-disk
format documentation in the ext4 tree. So it would be easier if we
keep ext4 documentation updates in the ext4 git tree.
Thanks,
- Ted
^ permalink raw reply
* [PATCH v1 1/2] vsprintf: introduce %dE for error constants
From: Uwe Kleine-König @ 2019-08-24 23:37 UTC (permalink / raw)
To: Andrew Morton
Cc: Jonathan Corbet, linux-doc, linux-kernel, linux-gpio,
Linus Walleij, Bartosz Golaszewski
pr_info("probing failed (%dE)\n", ret);
expands to
probing failed (EIO)
if ret holds -EIO (or EIO). This introduces an array of error codes. If
the error code is missing, %dE falls back to %d and so prints the plain
number.
Signed-off-by: Uwe Kleine-König <uwe@kleine-koenig.org>
---
Hello
there are many code sites that benefit from this. Just grep for
"(%d)" ...
As an example the follow up patch converts a printk to use this new
format escape.
Best regards
Uwe
Documentation/core-api/printk-formats.rst | 3 +
lib/vsprintf.c | 193 +++++++++++++++++++++-
2 files changed, 195 insertions(+), 1 deletion(-)
diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst
index c6224d039bcb..81002414f956 100644
--- a/Documentation/core-api/printk-formats.rst
+++ b/Documentation/core-api/printk-formats.rst
@@ -35,6 +35,9 @@ Integer types
u64 %llu or %llx
+To print the name that corresponds to an integer error constant, use %dE and
+pass the int.
+
If <type> is dependent on a config option for its size (e.g., sector_t,
blkcnt_t) or is architecture-dependent for its size (e.g., tcflag_t), use a
format specifier of its largest possible type and explicitly cast to it.
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index b0967cf17137..672eab8dab84 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -533,6 +533,192 @@ char *number(char *buf, char *end, unsigned long long num,
return buf;
}
+#define ERRORCODE(x) { .str = #x, .err = x }
+
+static const struct {
+ const char *str;
+ int err;
+} errorcodes[] = {
+ ERRORCODE(EPERM),
+ ERRORCODE(ENOENT),
+ ERRORCODE(ESRCH),
+ ERRORCODE(EINTR),
+ ERRORCODE(EIO),
+ ERRORCODE(ENXIO),
+ ERRORCODE(E2BIG),
+ ERRORCODE(ENOEXEC),
+ ERRORCODE(EBADF),
+ ERRORCODE(ECHILD),
+ ERRORCODE(EAGAIN),
+ ERRORCODE(ENOMEM),
+ ERRORCODE(EACCES),
+ ERRORCODE(EFAULT),
+ ERRORCODE(ENOTBLK),
+ ERRORCODE(EBUSY),
+ ERRORCODE(EEXIST),
+ ERRORCODE(EXDEV),
+ ERRORCODE(ENODEV),
+ ERRORCODE(ENOTDIR),
+ ERRORCODE(EISDIR),
+ ERRORCODE(EINVAL),
+ ERRORCODE(ENFILE),
+ ERRORCODE(EMFILE),
+ ERRORCODE(ENOTTY),
+ ERRORCODE(ETXTBSY),
+ ERRORCODE(EFBIG),
+ ERRORCODE(ENOSPC),
+ ERRORCODE(ESPIPE),
+ ERRORCODE(EROFS),
+ ERRORCODE(EMLINK),
+ ERRORCODE(EPIPE),
+ ERRORCODE(EDOM),
+ ERRORCODE(ERANGE),
+ ERRORCODE(EDEADLK),
+ ERRORCODE(ENAMETOOLONG),
+ ERRORCODE(ENOLCK),
+ ERRORCODE(ENOSYS),
+ ERRORCODE(ENOTEMPTY),
+ ERRORCODE(ELOOP),
+ ERRORCODE(EWOULDBLOCK),
+ ERRORCODE(ENOMSG),
+ ERRORCODE(EIDRM),
+ ERRORCODE(ECHRNG),
+ ERRORCODE(EL2NSYNC),
+ ERRORCODE(EL3HLT),
+ ERRORCODE(EL3RST),
+ ERRORCODE(ELNRNG),
+ ERRORCODE(EUNATCH),
+ ERRORCODE(ENOCSI),
+ ERRORCODE(EL2HLT),
+ ERRORCODE(EBADE),
+ ERRORCODE(EBADR),
+ ERRORCODE(EXFULL),
+ ERRORCODE(ENOANO),
+ ERRORCODE(EBADRQC),
+ ERRORCODE(EBADSLT),
+ ERRORCODE(EBFONT),
+ ERRORCODE(ENOSTR),
+ ERRORCODE(ENODATA),
+ ERRORCODE(ETIME),
+ ERRORCODE(ENOSR),
+ ERRORCODE(ENONET),
+ ERRORCODE(ENOPKG),
+ ERRORCODE(EREMOTE),
+ ERRORCODE(ENOLINK),
+ ERRORCODE(EADV),
+ ERRORCODE(ESRMNT),
+ ERRORCODE(ECOMM),
+ ERRORCODE(EPROTO),
+ ERRORCODE(EMULTIHOP),
+ ERRORCODE(EDOTDOT),
+ ERRORCODE(EBADMSG),
+ ERRORCODE(EOVERFLOW),
+ ERRORCODE(ENOTUNIQ),
+ ERRORCODE(EBADFD),
+ ERRORCODE(EREMCHG),
+ ERRORCODE(ELIBACC),
+ ERRORCODE(ELIBBAD),
+ ERRORCODE(ELIBSCN),
+ ERRORCODE(ELIBMAX),
+ ERRORCODE(ELIBEXEC),
+ ERRORCODE(EILSEQ),
+ ERRORCODE(ERESTART),
+ ERRORCODE(ESTRPIPE),
+ ERRORCODE(EUSERS),
+ ERRORCODE(ENOTSOCK),
+ ERRORCODE(EDESTADDRREQ),
+ ERRORCODE(EMSGSIZE),
+ ERRORCODE(EPROTOTYPE),
+ ERRORCODE(ENOPROTOOPT),
+ ERRORCODE(EPROTONOSUPPORT),
+ ERRORCODE(ESOCKTNOSUPPORT),
+ ERRORCODE(EOPNOTSUPP),
+ ERRORCODE(EPFNOSUPPORT),
+ ERRORCODE(EAFNOSUPPORT),
+ ERRORCODE(EADDRINUSE),
+ ERRORCODE(EADDRNOTAVAIL),
+ ERRORCODE(ENETDOWN),
+ ERRORCODE(ENETUNREACH),
+ ERRORCODE(ENETRESET),
+ ERRORCODE(ECONNABORTED),
+ ERRORCODE(ECONNRESET),
+ ERRORCODE(ENOBUFS),
+ ERRORCODE(EISCONN),
+ ERRORCODE(ENOTCONN),
+ ERRORCODE(ESHUTDOWN),
+ ERRORCODE(ETOOMANYREFS),
+ ERRORCODE(ETIMEDOUT),
+ ERRORCODE(ECONNREFUSED),
+ ERRORCODE(EHOSTDOWN),
+ ERRORCODE(EHOSTUNREACH),
+ ERRORCODE(EALREADY),
+ ERRORCODE(EINPROGRESS),
+ ERRORCODE(ESTALE),
+ ERRORCODE(EUCLEAN),
+ ERRORCODE(ENOTNAM),
+ ERRORCODE(ENAVAIL),
+ ERRORCODE(EISNAM),
+ ERRORCODE(EREMOTEIO),
+ ERRORCODE(EDQUOT),
+ ERRORCODE(ENOMEDIUM),
+ ERRORCODE(EMEDIUMTYPE),
+ ERRORCODE(ECANCELED),
+ ERRORCODE(ENOKEY),
+ ERRORCODE(EKEYEXPIRED),
+ ERRORCODE(EKEYREVOKED),
+ ERRORCODE(EKEYREJECTED),
+ ERRORCODE(EOWNERDEAD),
+ ERRORCODE(ENOTRECOVERABLE),
+ ERRORCODE(ERFKILL),
+ ERRORCODE(EHWPOISON),
+ ERRORCODE(ERESTARTSYS),
+ ERRORCODE(ERESTARTNOINTR),
+ ERRORCODE(ERESTARTNOHAND),
+ ERRORCODE(ENOIOCTLCMD),
+ ERRORCODE(ERESTART_RESTARTBLOCK),
+ ERRORCODE(EPROBE_DEFER),
+ ERRORCODE(EOPENSTALE),
+ ERRORCODE(ENOPARAM),
+ ERRORCODE(EBADHANDLE),
+ ERRORCODE(ENOTSYNC),
+ ERRORCODE(EBADCOOKIE),
+ ERRORCODE(ENOTSUPP),
+ ERRORCODE(ETOOSMALL),
+ ERRORCODE(ESERVERFAULT),
+ ERRORCODE(EBADTYPE),
+ ERRORCODE(EJUKEBOX),
+ ERRORCODE(EIOCBQUEUED),
+ ERRORCODE(ERECALLCONFLICT),
+};
+
+static noinline_for_stack
+char *errstr(char *buf, char *end, unsigned long long num,
+ struct printf_spec spec)
+{
+ char *errname = NULL;
+ size_t errnamelen, copy;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(errorcodes); ++i) {
+ if (num == errorcodes[i].err || num == -errorcodes[i].err) {
+ errname = errorcodes[i].str;
+ break;
+ }
+ }
+
+ if (!errname) {
+ /* fall back to ordinary number */
+ return number(buf, end, num, spec);
+ }
+
+ copy = errnamelen = strlen(errname);
+ if (copy > end - buf)
+ copy = end - buf;
+ buf = memcpy(buf, errname, copy);
+
+ return buf + errnamelen;
+}
+
static noinline_for_stack
char *special_hex_number(char *buf, char *end, unsigned long long num, int size)
{
@@ -2566,7 +2752,12 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
num = va_arg(args, unsigned int);
}
- str = number(str, end, num, spec);
+ if (spec.type == FORMAT_TYPE_INT && *fmt == 'E') {
+ fmt++;
+ str = errstr(str, end, num, spec);
+ } else {
+ str = number(str, end, num, spec);
+ }
}
}
--
2.20.1
^ permalink raw reply related
* [PATCH v1 2/2] gpiolib: print an error name instead of a plain number in error string
From: Uwe Kleine-König @ 2019-08-24 23:37 UTC (permalink / raw)
To: Andrew Morton, Linus Walleij, Bartosz Golaszewski
Cc: Jonathan Corbet, linux-doc, linux-kernel, linux-gpio
In-Reply-To: <20190824233724.1775-1-uwe@kleine-koenig.org>
This is an example that makes use of the just introduced printk format
%dE that prints (e.g.) "EIO" when the matching integer is -EIO (or EIO).
Signed-off-by: Uwe Kleine-König <uwe@kleine-koenig.org>
---
drivers/gpio/gpiolib.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index f497003f119c..b50ea24f087f 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1247,7 +1247,7 @@ static void gpiochip_setup_devs(void)
list_for_each_entry(gdev, &gpio_devices, list) {
err = gpiochip_setup_dev(gdev);
if (err)
- pr_err("%s: Failed to initialize gpio device (%d)\n",
+ pr_err("%s: Failed to initialize gpio device (%dE)\n",
dev_name(&gdev->dev), err);
}
}
--
2.20.1
^ permalink raw reply related
* Re: [PATCH v1 1/2] vsprintf: introduce %dE for error constants
From: Andrew Morton @ 2019-08-24 23:58 UTC (permalink / raw)
To: Uwe Kleine-König
Cc: Jonathan Corbet, linux-doc, linux-kernel, linux-gpio,
Linus Walleij, Bartosz Golaszewski, Petr Mladek,
Sergey Senozhatsky, Steven Rostedt
In-Reply-To: <20190824233724.1775-1-uwe@kleine-koenig.org>
(cc printk maintainers).
On Sun, 25 Aug 2019 01:37:23 +0200 Uwe Kleine-König <uwe@kleine-koenig.org> wrote:
> pr_info("probing failed (%dE)\n", ret);
>
> expands to
>
> probing failed (EIO)
>
> if ret holds -EIO (or EIO). This introduces an array of error codes. If
> the error code is missing, %dE falls back to %d and so prints the plain
> number.
Huh. I'm surprised we don't already have this. Seems that this will
be applicable in a lot of places? Although we shouldn't go blindly
converting everything in sight - that would risk breaking userspace
which parses kernel strings.
Is it really necessary to handle the positive errnos? Does much kernel
code actually do that (apart from kernel code which is buggy)?
> Signed-off-by: Uwe Kleine-König <uwe@kleine-koenig.org>
> ---
> Hello
>
> there are many code sites that benefit from this. Just grep for
> "(%d)" ...
Yup. This observation shouldn't be below the "^---$" ;) An approximate
grep|wc would be interesting.
> --- a/lib/vsprintf.c
> +++ b/lib/vsprintf.c
> @@ -533,6 +533,192 @@ char *number(char *buf, char *end, unsigned long long num,
> return buf;
> }
>
> +#define ERRORCODE(x) { .str = #x, .err = x }
> +
> +static const struct {
> + const char *str;
> + int err;
> +} errorcodes[] = {
It's a bit of a hack, but an array of char*'s and a separate array of
ushorts would save a bit of space.
> + ERRORCODE(EPERM),
> + ERRORCODE(ENOENT),
> + ERRORCODE(ESRCH),
>
> ...
>
> +static noinline_for_stack
Why this? I'm suspecting this will actually increase stack use?
> +char *errstr(char *buf, char *end, unsigned long long num,
> + struct printf_spec spec)
> +{
> + char *errname = NULL;
> + size_t errnamelen, copy;
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(errorcodes); ++i) {
> + if (num == errorcodes[i].err || num == -errorcodes[i].err) {
> + errname = errorcodes[i].str;
> + break;
> + }
> + }
> +
> + if (!errname) {
> + /* fall back to ordinary number */
> + return number(buf, end, num, spec);
> + }
> +
> + copy = errnamelen = strlen(errname);
> + if (copy > end - buf)
> + copy = end - buf;
> + buf = memcpy(buf, errname, copy);
> +
> + return buf + errnamelen;
> +}
OK, I guess `errstr' is an OK name for a static function and we can use
this to add a new strerror() should the need arise.
>
> ...
>
^ permalink raw reply
* [PATCH 1/2] kbuild: remove clean-dirs syntax
From: Masahiro Yamada @ 2019-08-25 1:31 UTC (permalink / raw)
To: linux-kbuild
Cc: Masahiro Yamada, Jonathan Corbet, Michal Marek, linux-doc,
linux-kernel
The only the difference between clean-files and clean-dirs is the -r
option passed to the 'rm' command.
You can always pass -r, and then remove the clean-dirs syntax.
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---
Documentation/kbuild/makefiles.rst | 16 ++++------------
scripts/Makefile.clean | 16 ++--------------
scripts/kconfig/Makefile | 2 +-
usr/include/Makefile | 4 +---
4 files changed, 8 insertions(+), 30 deletions(-)
diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst
index 68ed20ef37dd..78aa51a6fcd4 100644
--- a/Documentation/kbuild/makefiles.rst
+++ b/Documentation/kbuild/makefiles.rst
@@ -765,7 +765,8 @@ Files matching the patterns "*.[oas]", "*.ko", plus some additional files
generated by kbuild are deleted all over the kernel src tree when
"make clean" is executed.
-Additional files can be specified in kbuild makefiles by use of $(clean-files).
+Additional files or directories can be specified in kbuild makefiles by use of
+$(clean-files).
Example::
@@ -776,17 +777,8 @@ When executing "make clean", the file "crc32table.h" will be deleted.
Kbuild will assume files to be in the same relative directory as the
Makefile, except if prefixed with $(objtree).
-To delete a directory hierarchy use:
-
- Example::
-
- #scripts/package/Makefile
- clean-dirs := $(objtree)/debian/
-
-This will delete the directory debian in the toplevel directory, including all
-subdirectories.
-
-To exclude certain files from make clean, use the $(no-clean-files) variable.
+To exclude certain files or directories from make clean, use the
+$(no-clean-files) variable.
Usually kbuild descends down in subdirectories due to "obj-* := dir/",
but in the architecture makefiles where the kbuild infrastructure
diff --git a/scripts/Makefile.clean b/scripts/Makefile.clean
index 0b80e3207b20..cbfbe13dc87d 100644
--- a/scripts/Makefile.clean
+++ b/scripts/Makefile.clean
@@ -52,26 +52,14 @@ __clean-files := $(wildcard \
$(addprefix $(obj)/, $(filter-out $(objtree)/%, $(__clean-files))) \
$(filter $(objtree)/%, $(__clean-files)))
-# same as clean-files
-
-__clean-dirs := $(wildcard \
- $(addprefix $(obj)/, $(filter-out $(objtree)/%, $(clean-dirs))) \
- $(filter $(objtree)/%, $(clean-dirs)))
-
# ==========================================================================
-quiet_cmd_clean = CLEAN $(obj)
- cmd_clean = rm -f $(__clean-files)
-quiet_cmd_cleandir = CLEAN $(__clean-dirs)
- cmd_cleandir = rm -rf $(__clean-dirs)
-
+quiet_cmd_clean = CLEAN $(obj)
+ cmd_clean = rm -rf $(__clean-files)
__clean: $(subdir-ymn)
ifneq ($(strip $(__clean-files)),)
+$(call cmd,clean)
-endif
-ifneq ($(strip $(__clean-dirs)),)
- +$(call cmd,cleandir)
endif
@:
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index 7656e1137b6b..bed7a5a2fbe9 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -114,7 +114,7 @@ testconfig: $(obj)/conf
$(PYTHON3) -B -m pytest $(srctree)/$(src)/tests \
-o cache_dir=$(abspath $(obj)/tests/.cache) \
$(if $(findstring 1,$(KBUILD_VERBOSE)),--capture=no)
-clean-dirs += tests/.cache
+clean-files += tests/.cache
# Help text used by make help
help:
diff --git a/usr/include/Makefile b/usr/include/Makefile
index 1fb6abe29b2f..05c71ef42f51 100644
--- a/usr/include/Makefile
+++ b/usr/include/Makefile
@@ -115,6 +115,4 @@ header-test-y += $(filter-out $(header-test-), \
$(patsubst $(obj)/%,%, $(wildcard \
$(addprefix $(obj)/, *.h */*.h */*/*.h */*/*/*.h))))
-# For GNU Make <= 4.2.1, $(wildcard $(obj)/*/) matches to not only directories
-# but also regular files. Use $(filter %/, ...) just in case.
-clean-dirs += $(patsubst $(obj)/%/,%,$(filter %/, $(wildcard $(obj)/*/)))
+clean-files += $(filter-out Makefile, $(notdir $(wildcard $(obj)/*)))
--
2.17.1
^ permalink raw reply related
* Re: [PATCH v1 1/2] vsprintf: introduce %dE for error constants
From: Uwe Kleine-König @ 2019-08-25 9:14 UTC (permalink / raw)
To: Andrew Morton
Cc: Jonathan Corbet, linux-doc, linux-kernel, linux-gpio,
Linus Walleij, Bartosz Golaszewski, Petr Mladek,
Sergey Senozhatsky, Steven Rostedt
In-Reply-To: <20190824165829.7d330367992c62dab87f6652@linux-foundation.org>
[-- Attachment #1: Type: text/plain, Size: 5632 bytes --]
Hello Andrew,
On Sat, Aug 24, 2019 at 04:58:29PM -0700, Andrew Morton wrote:
> (cc printk maintainers).
Ah, I wasn't aware there is something like them. Thanks
> On Sun, 25 Aug 2019 01:37:23 +0200 Uwe Kleine-König <uwe@kleine-koenig.org> wrote:
>
> > pr_info("probing failed (%dE)\n", ret);
> >
> > expands to
> >
> > probing failed (EIO)
> >
> > if ret holds -EIO (or EIO). This introduces an array of error codes. If
> > the error code is missing, %dE falls back to %d and so prints the plain
> > number.
>
> Huh. I'm surprised we don't already have this. Seems that this will
> be applicable in a lot of places? Although we shouldn't go blindly
> converting everything in sight - that would risk breaking userspace
> which parses kernel strings.
Uah, even the kernel log is API? But I agree so far that this is merge
window material and shouldn't make it into v5.3 :-)
> Is it really necessary to handle the positive errnos? Does much kernel
> code actually do that (apart from kernel code which is buggy)?
I didn't check; probably not. But the whole positive range seems so
unused and interpreting EIO (and not only -EIO) as "EIO" seems straight
forward. But I don't feel strong either way.
> > Signed-off-by: Uwe Kleine-König <uwe@kleine-koenig.org>
> > ---
> > Hello
> >
> > there are many code sites that benefit from this. Just grep for
> > "(%d)" ...
>
> Yup. This observation shouldn't be below the "^---$" ;) An approximate
> grep|wc would be interesting.
I didn't check how many false positives there are using "(%d)", but I'd
use an a bit more elaborate expression for the commit log:
$ git grep '(%d)' | wc -l
7336
$ git grep -E '^\s*(printk|(kv?as|sn?|vs(c?n)?)printf|(kvm|dev|pr)_(emerg|alert|crit|err|warn(ing)?|notice|info|cont|devel|debug|dbg))\(.*(\(%d\)|: %d)\\n' | wc -l
9140
The latter matches several printk-variants emitting a string ending in
one of
'(%d)\n' (1839 matches)
': %d\n' (7301 matches)
. I would expect that many of the 7336 matches for '(%d)' that are not
matched by the longer expression are false negatives because the
function name is in the previous line. So I would estimate around 10k
strings that could benefit from %dE.
> > --- a/lib/vsprintf.c
> > +++ b/lib/vsprintf.c
> > @@ -533,6 +533,192 @@ char *number(char *buf, char *end, unsigned long long num,
> > return buf;
> > }
> >
> > +#define ERRORCODE(x) { .str = #x, .err = x }
> > +
> > +static const struct {
> > + const char *str;
> > + int err;
> > +} errorcodes[] = {
>
> It's a bit of a hack, but an array of char*'s and a separate array of
> ushorts would save a bit of space.
Hmm, true. Currently we have 150 entries taking 150 * (sizeof(void *) *
2). Is it worth to think about the hacky solution to go down to 150 *
(sizeof(void *) + 2)?
For an ARM build bloat-o-meter reports (comparing linus/master to linus/master
+ my patch):
add/remove: 2/0 grow/shrink: 4/2 up/down: 1488/-8 (1480)
Function old new delta
errorcodes - 1200 +1200
errstr - 200 +200
vsnprintf 884 960 +76
set_precision 148 152 +4
resource_string 1380 1384 +4
flags_string 400 404 +4
num_to_str 288 284 -4
format_decode 1024 1020 -4
Total: Before=21686, After=23166, chg +6.82%
But that doesn't seem to include the size increase for all the added
strings which seems to be around another 1300 bytes.
> > + ERRORCODE(EPERM),
> > + ERRORCODE(ENOENT),
> > + ERRORCODE(ESRCH),
> >
> > ...
> >
> > +static noinline_for_stack
>
> Why this? I'm suspecting this will actually increase stack use?
I don't know what it does, just copied it from number() which is used
similarly.
> > +char *errstr(char *buf, char *end, unsigned long long num,
> > + struct printf_spec spec)
> > +{
> > + char *errname = NULL;
I missed a warning during my tests, there is a const missing in this
line.
> > + size_t errnamelen, copy;
> > + int i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(errorcodes); ++i) {
> > + if (num == errorcodes[i].err || num == -errorcodes[i].err) {
> > + errname = errorcodes[i].str;
> > + break;
> > + }
> > + }
> > +
> > + if (!errname) {
> > + /* fall back to ordinary number */
> > + return number(buf, end, num, spec);
> > + }
> > +
> > + copy = errnamelen = strlen(errname);
> > + if (copy > end - buf)
> > + copy = end - buf;
> > + buf = memcpy(buf, errname, copy);
> > +
> > + return buf + errnamelen;
> > +}
>
> OK, I guess `errstr' is an OK name for a static function
IMHO the name is very generic (which is bad), but it is in good company,
as there is also pointer() and number().
> and we can use this to add a new strerror() should the need arise.
In userspace the purpose of strerror is different. It would yield "I/O
error" not "EIO". So strerror using this array would only be a "strerror
light".
In my first prototype I even used %m instead of %dE, but as %m (in
glibc) doesn't consume an argument and produces the more verbose
variant, I changed my mind and went for %dE. (Also my patch has the
undocumented side effect that you can use %ldE if the error is held by a
long int. I didn't test that though.)
Best regards
Uwe
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2] fbdev: fbmem: allow overriding the number of bootup logos
From: Peter Rosin @ 2019-08-25 9:35 UTC (permalink / raw)
To: Matthew Wilcox
Cc: linux-kernel@vger.kernel.org, Bartlomiej Zolnierkiewicz,
Jonathan Corbet, dri-devel@lists.freedesktop.org,
linux-fbdev@vger.kernel.org, linux-doc@vger.kernel.org
In-Reply-To: <20190824153446.GB28002@bombadil.infradead.org>
On 2019-08-24 17:34, Matthew Wilcox wrote:
> On Fri, Aug 23, 2019 at 08:47:47AM +0000, Peter Rosin wrote:
>> +++ b/drivers/video/fbdev/core/fbcon.c
>> +++ b/drivers/video/fbdev/core/fbmem.c
>> @@ -56,6 +56,9 @@ EXPORT_SYMBOL(num_registered_fb);
>> bool fb_center_logo __read_mostly;
>> EXPORT_SYMBOL(fb_center_logo);
>>
>> +unsigned int fb_logo_count __read_mostly;
>> +EXPORT_SYMBOL(fb_logo_count);
>
> Why does this symbol need to be exported? As I read the Makefile, fbcon
> and fbmem are combined into the same module, so while the symbol needs
> to be non-static, it doesn't need to be exported to other modules.
I guess you are right. I'll send a v2 tomorrow with an added patch to
unexport the fb_center_logo variable while at it...
Thanks for the feedback.
Cheers,
Peter
^ permalink raw reply
* [PATCH 00/11] ftrace: add support for recording function parameters and return value
From: Changbin Du @ 2019-08-25 13:23 UTC (permalink / raw)
To: Steven Rostedt, Ingo Molnar
Cc: Jonathan Corbet, Jessica Yu, Thomas Gleixner, x86, linux-doc,
linux-kernel, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
linux-arch, linux-kbuild, Changbin Du
This series introduces a new ftrace feature CONFIG_FTRACE_FUNC_PROTOTYPE to
record function parameters and return value. It can be enabled/disabled at
runtime by a new trace option "record-funcproto".
To achieve this, we need the function prototype infomation and the location of
parameters. A new tool scripts/ftrace/funcprototype is provided to collect such
necessary infomation and put them into kernel. It walks through the DWARF debug
sections in each object file and output assembly code which defines the function
prototype data. Then the assembly code is built into the original object file.
Here is an example of memremap() function:
.section __funcprotostr, "a"
.P_memremap_0:
.string "offset"
.P_memremap_1:
.string "size"
.P_memremap_2:
.string "flags"
.section __funcproto, "a"
.quad memremap
.byte 0x8
.byte 0x3
.quad .P_memremap_0
.byte 0x8
.byte 0x55
.byte 0x0
.quad .P_memremap_1
.byte 0x8
.byte 0x54
.byte 0x0
.quad .P_memremap_2
.byte 0x8
.byte 0x51
.byte 0x0
Note that currently funcprototype only support global functions. Local functions
can also be supported by using the idea described in recordmcount.pl - temporary
change local functions to global.
The C ABI is arch specific. For arch which supports this feature must
implement a new arch-specific interface arch_fgraph_record_params() and
deliver the return value of function to ftrace core part.
This series only add support for x86_64 platform. Other platforms can be
supported in the future.
Here is an example of the graph trace of function pick_next_task_fair().
Note that we only record the parameter and return value of global
functions.
2) | pick_next_task_fair() {
2) | update_blocked_averages() {
2) 0.765 us | _raw_spin_lock_irqsave(lock=0xffff88807da2b100); /* ret=0x0000000000000082 */
2) 0.944 us | update_rq_clock(rq=0xffff88807da2b100);
2) 0.612 us | __update_load_avg_cfs_rq(now=0x000000251b8516ee, cfs_rq=0xffff8880754f7488); /* ret=0 */
2) 0.654 us | __update_load_avg_se(now=0x000000251b8516ee, cfs_rq=0xffff88807da2b180, se=0xffff88807be2e0d8); /* ret=0 */
2) 0.206 us | __update_load_avg_cfs_rq(now=0x000000251b8516ee, cfs_rq=0xffff88807da2b180); /* ret=0 */
2) | __update_load_avg_cfs_rq(now=0x000000251b8516ee, cfs_rq=0xffff888079b5fb18) {
2) 2.410 us | __accumulate_pelt_segments();
2) 3.103 us | } /* ret=1 */
2) 0.193 us | __update_load_avg_cfs_rq(now=0x000000251b8516ee, cfs_rq=0xffff88807da2b180); /* ret=0 */
2) | update_rt_rq_load_avg(now=0x000000251b8516ee, rq=0xffff88807da2b100, running=0) {
2) 0.258 us | __accumulate_pelt_segments();
2) 1.617 us | } /* ret=1 */
2) | update_dl_rq_load_avg(now=0x000000251b8516ee, rq=0xffff88807da2b100, running=0) {
2) 0.230 us | __accumulate_pelt_segments();
2) 1.511 us | } /* ret=1 */
2) 1.040 us | _raw_spin_unlock_irqrestore(lock=0xffff88807da2b100, flags=0x0000000000000082);
2) + 14.739 us | }
2) | load_balance() {
2) | find_busiest_group() {
2) 0.874 us | update_group_capacity(sd=0xffff88807c1d37d0, cpu=2);
2) 1.761 us | idle_cpu();
2) 0.262 us | idle_cpu();
2) 0.217 us | idle_cpu();
2) 6.338 us | }
2) 8.442 us | }
2) 1.823 us | __msecs_to_jiffies(m=0x00000006); /* ret=0x0000000000000002 */
2) | load_balance() {
2) | find_busiest_group() {
2) 0.434 us | idle_cpu();
2) 0.233 us | idle_cpu();
2) 0.210 us | idle_cpu();
2) 2.308 us | }
2) 2.821 us | }
2) 0.263 us | __msecs_to_jiffies(m=0x00000008); /* ret=0x0000000000000002 */
2) 0.977 us | _raw_spin_lock(lock=0xffff88807da2b100);
2) + 32.262 us | }
The printing rules of each value is:
o For signed value, it is always printed as decimal number.
o For unsigned value,
- For value has size great than 8, it is printed as '{..}'.
- For value has size of 1,2,4,8, it is printed as hexadecimal number.
- If failed to record a parameter, it is printed as '?'.
Changbin Du (11):
ftrace: move recordmcount tools to scripts/ftrace
ftrace: introduce new building tool funcprototype
asm-generic: add generic dwarf definition
ftrace/hash: add private data field
ftrace: create memcache for hash entries
ftrace: process function prototype data in vmlinux and modules
ftrace: prepare arch specific interfaces for function prototype
feature
ftrace: introduce core part of function prototype recording
x86_64: add function prototype recording support
ftrace: add doc for new option record-funcproto
MAINTAINERS: make scripts/ftrace/ maintained
Documentation/trace/ftrace.rst | 6 +
MAINTAINERS | 2 +
arch/arm/kernel/ftrace.c | 2 +-
arch/arm64/kernel/ftrace.c | 2 +-
arch/csky/kernel/ftrace.c | 2 +-
arch/microblaze/kernel/ftrace.c | 2 +-
arch/mips/kernel/ftrace.c | 2 +-
arch/nds32/kernel/ftrace.c | 5 +-
arch/parisc/kernel/ftrace.c | 2 +-
arch/powerpc/kernel/trace/ftrace.c | 2 +-
arch/riscv/kernel/ftrace.c | 2 +-
arch/s390/kernel/ftrace.c | 2 +-
arch/sh/kernel/ftrace.c | 2 +-
arch/sparc/kernel/ftrace.c | 2 +-
arch/x86/Kconfig | 1 +
arch/x86/kernel/ftrace.c | 84 +++-
arch/x86/kernel/ftrace_64.S | 4 +-
include/asm-generic/dwarf.h | 199 +++++++++
include/asm-generic/vmlinux.lds.h | 18 +
include/linux/ftrace.h | 55 ++-
include/linux/module.h | 4 +
kernel/module.c | 25 +-
kernel/trace/Kconfig | 19 +
kernel/trace/fgraph.c | 26 +-
kernel/trace/ftrace.c | 164 +++++++-
kernel/trace/trace.h | 20 +-
kernel/trace/trace_entries.h | 10 +
kernel/trace/trace_functions_graph.c | 108 ++++-
kernel/trace/trace_irqsoff.c | 3 +-
kernel/trace/trace_sched_wakeup.c | 3 +-
scripts/.gitignore | 1 -
scripts/Makefile | 2 +-
scripts/Makefile.build | 28 +-
scripts/ftrace/.gitignore | 6 +
scripts/ftrace/Makefile | 9 +
scripts/ftrace/funcprototype.c | 576 +++++++++++++++++++++++++++
scripts/{ => ftrace}/recordmcount.c | 0
scripts/{ => ftrace}/recordmcount.h | 0
scripts/{ => ftrace}/recordmcount.pl | 0
39 files changed, 1340 insertions(+), 60 deletions(-)
create mode 100644 include/asm-generic/dwarf.h
create mode 100644 scripts/ftrace/.gitignore
create mode 100644 scripts/ftrace/Makefile
create mode 100644 scripts/ftrace/funcprototype.c
rename scripts/{ => ftrace}/recordmcount.c (100%)
rename scripts/{ => ftrace}/recordmcount.h (100%)
rename scripts/{ => ftrace}/recordmcount.pl (100%)
mode change 100755 => 100644
--
2.20.1
^ permalink raw reply
* [PATCH 01/11] ftrace: move recordmcount tools to scripts/ftrace
From: Changbin Du @ 2019-08-25 13:23 UTC (permalink / raw)
To: Steven Rostedt, Ingo Molnar
Cc: Jonathan Corbet, Jessica Yu, Thomas Gleixner, x86, linux-doc,
linux-kernel, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
linux-arch, linux-kbuild, Changbin Du, John F . Reiser
In-Reply-To: <20190825132330.5015-1-changbin.du@gmail.com>
Move ftrace tools to its own directory. We will add another tool later.
Cc: John F. Reiser <jreiser@BitWagon.com>
Signed-off-by: Changbin Du <changbin.du@gmail.com>
---
scripts/.gitignore | 1 -
scripts/Makefile | 2 +-
scripts/Makefile.build | 10 +++++-----
scripts/ftrace/.gitignore | 4 ++++
scripts/ftrace/Makefile | 4 ++++
scripts/{ => ftrace}/recordmcount.c | 0
scripts/{ => ftrace}/recordmcount.h | 0
scripts/{ => ftrace}/recordmcount.pl | 0
8 files changed, 14 insertions(+), 7 deletions(-)
create mode 100644 scripts/ftrace/.gitignore
create mode 100644 scripts/ftrace/Makefile
rename scripts/{ => ftrace}/recordmcount.c (100%)
rename scripts/{ => ftrace}/recordmcount.h (100%)
rename scripts/{ => ftrace}/recordmcount.pl (100%)
mode change 100755 => 100644
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 17f8cef88fa8..1b5b5d595d80 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -6,7 +6,6 @@ conmakehash
kallsyms
pnmtologo
unifdef
-recordmcount
sortextable
asn1_compiler
extract-cert
diff --git a/scripts/Makefile b/scripts/Makefile
index 16bcb8087899..d5992def49a8 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -14,7 +14,6 @@ hostprogs-$(CONFIG_BUILD_BIN2C) += bin2c
hostprogs-$(CONFIG_KALLSYMS) += kallsyms
hostprogs-$(CONFIG_LOGO) += pnmtologo
hostprogs-$(CONFIG_VT) += conmakehash
-hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable
hostprogs-$(CONFIG_ASN1) += asn1_compiler
hostprogs-$(CONFIG_MODULE_SIG) += sign-file
@@ -34,6 +33,7 @@ hostprogs-y += unifdef
subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins
subdir-$(CONFIG_MODVERSIONS) += genksyms
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+subdir-$(CONFIG_FTRACE) += ftrace
# Let clean descend into subdirs
subdir- += basic dtc gdb kconfig mod package
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 2f66ed388d1c..67558983c518 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -188,18 +188,18 @@ endif
# files, including recordmcount.
sub_cmd_record_mcount = \
if [ $(@) != "scripts/mod/empty.o" ]; then \
- $(objtree)/scripts/recordmcount $(RECORDMCOUNT_FLAGS) "$(@)"; \
+ $(objtree)/scripts/ftrace/recordmcount $(RECORDMCOUNT_FLAGS) "$(@)"; \
fi;
-recordmcount_source := $(srctree)/scripts/recordmcount.c \
- $(srctree)/scripts/recordmcount.h
+recordmcount_source := $(srctree)/scripts/ftrace/recordmcount.c \
+ $(srctree)/scripts/ftrace/recordmcount.h
else
-sub_cmd_record_mcount = perl $(srctree)/scripts/recordmcount.pl "$(ARCH)" \
+sub_cmd_record_mcount = perl $(srctree)/scripts/ftrace/recordmcount.pl "$(ARCH)" \
"$(if $(CONFIG_CPU_BIG_ENDIAN),big,little)" \
"$(if $(CONFIG_64BIT),64,32)" \
"$(OBJDUMP)" "$(OBJCOPY)" "$(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)" \
"$(LD) $(KBUILD_LDFLAGS)" "$(NM)" "$(RM)" "$(MV)" \
"$(if $(part-of-module),1,0)" "$(@)";
-recordmcount_source := $(srctree)/scripts/recordmcount.pl
+recordmcount_source := $(srctree)/scripts/ftrace/recordmcount.pl
endif # BUILD_C_RECORDMCOUNT
cmd_record_mcount = $(if $(findstring $(strip $(CC_FLAGS_FTRACE)),$(_c_flags)), \
$(sub_cmd_record_mcount))
diff --git a/scripts/ftrace/.gitignore b/scripts/ftrace/.gitignore
new file mode 100644
index 000000000000..54d582c8faad
--- /dev/null
+++ b/scripts/ftrace/.gitignore
@@ -0,0 +1,4 @@
+#
+# Generated files
+#
+recordmcount
diff --git a/scripts/ftrace/Makefile b/scripts/ftrace/Makefile
new file mode 100644
index 000000000000..6797e51473e5
--- /dev/null
+++ b/scripts/ftrace/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
+always := $(hostprogs-y)
diff --git a/scripts/recordmcount.c b/scripts/ftrace/recordmcount.c
similarity index 100%
rename from scripts/recordmcount.c
rename to scripts/ftrace/recordmcount.c
diff --git a/scripts/recordmcount.h b/scripts/ftrace/recordmcount.h
similarity index 100%
rename from scripts/recordmcount.h
rename to scripts/ftrace/recordmcount.h
diff --git a/scripts/recordmcount.pl b/scripts/ftrace/recordmcount.pl
old mode 100755
new mode 100644
similarity index 100%
rename from scripts/recordmcount.pl
rename to scripts/ftrace/recordmcount.pl
--
2.20.1
^ permalink raw reply related
* [PATCH 02/11] ftrace: introduce new building tool funcprototype
From: Changbin Du @ 2019-08-25 13:23 UTC (permalink / raw)
To: Steven Rostedt, Ingo Molnar
Cc: Jonathan Corbet, Jessica Yu, Thomas Gleixner, x86, linux-doc,
linux-kernel, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
linux-arch, linux-kbuild, Changbin Du
In-Reply-To: <20190825132330.5015-1-changbin.du@gmail.com>
This is a new ftrace tool to implement CONFIG_FTRACE_FUNC_PROTOTYPE
feature which allow ftrace record function parameters and return value
(see later patches).
Essentially funcprototype extracts only necessary information from
the DWARF debug sections in the ELF object file, including function
return type, parameter names and parameter data types. Then they will
be built into the original ELF object.
Here is an example for function memremap() in kernel/iomem.o. The
declaration is:
void *memremap(resource_size_t offset, size_t size, unsigned long flags)
The output of funcprototype tool is:
.section __funcprotostr, "a"
.P_memremap_0:
.string "offset"
.P_memremap_1:
.string "size"
.P_memremap_2:
.string "flags"
.section __funcproto, "a"
.quad memremap
.byte 0x8
.byte 0x3
.quad .P_memremap_0
.byte 0x8
.byte 0x55
.byte 0x0
.quad .P_memremap_1
.byte 0x8
.byte 0x54
.byte 0x0
.quad .P_memremap_2
.byte 0x8
.byte 0x51
.byte 0x0
The strings are placed in '__funcprotostr' section, and prototype
information is placed in '__funcproto' section. It equals to below
C struct:
struct func_param {
char *name;
uint8_t type;
uint8_t loc[2];
} __packed;
struct func_prototype {
unsigned long ip;
uint8_t ret_type;
uint8_t nr_param;
struct func_param params[0];
} __packed;
Signed-off-by: Changbin Du <changbin.du@gmail.com>
---
kernel/trace/Kconfig | 19 ++
scripts/Makefile.build | 18 +-
scripts/ftrace/.gitignore | 2 +
scripts/ftrace/Makefile | 7 +-
scripts/ftrace/funcprototype.c | 576 +++++++++++++++++++++++++++++++++
5 files changed, 620 insertions(+), 2 deletions(-)
create mode 100644 scripts/ftrace/funcprototype.c
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 98da8998c25c..20d1b0ae114d 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -38,6 +38,9 @@ config HAVE_FTRACE_MCOUNT_RECORD
help
See Documentation/trace/ftrace-design.rst
+config HAVE_FTRACE_FUNC_PROTOTYPE
+ bool
+
config HAVE_SYSCALL_TRACEPOINTS
bool
help
@@ -170,6 +173,22 @@ config FUNCTION_GRAPH_TRACER
the return value. This is done by setting the current return
address on the current task structure into a stack of calls.
+config FTRACE_FUNC_PROTOTYPE
+ bool "Support recording function parameters and return value"
+ default n
+ depends on DYNAMIC_FTRACE
+ depends on HAVE_FTRACE_FUNC_PROTOTYPE
+ depends on FUNCTION_GRAPH_TRACER
+ help
+ Enable the Function Graph Tracer to record function parameters and
+ return value. It can be dynamically enabled/disabled by the
+ 'record-funcproto' trace option.
+
+ By enabling this, function prototype information is built into
+ kernel. And the kernel size will increase by approximately 2%.
+
+ Say N if unsure.
+
config TRACE_PREEMPT_TOGGLE
bool
help
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 67558983c518..d56850808d96 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -206,6 +206,21 @@ cmd_record_mcount = $(if $(findstring $(strip $(CC_FLAGS_FTRACE)),$(_c_flags)),
endif # CC_USING_RECORD_MCOUNT
endif # CONFIG_FTRACE_MCOUNT_RECORD
+ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+sub_cmd_funcprototype = \
+ $(srctree)/scripts/ftrace/funcprototype "$(@)" | \
+ $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -c \
+ -o $(@D)/.tmp_$(@F:.o=.funcprototype) -x assembler -; \
+ $(LD) $(ld_flags) -r -o $(@D)/.tmp_$(@F) $@ $(@D)/.tmp_$(@F:.o=.funcprototype); \
+ mv -f $(@D)/.tmp_$(@F) $@; \
+ rm -f $(@D)/.tmp_$(@F:.o=.funcprototype);
+cmd_funcprototype = \
+ if $(OBJDUMP) -h $@ | grep -q __mcount_loc; then \
+ $(sub_cmd_funcprototype) \
+ fi
+funcprototype_source := $(srctree)/scripts/ftrace/funcprototype.c
+endif # CONFIG_FTRACE_FUNC_PROTOTYPE
+
ifdef CONFIG_STACK_VALIDATION
ifneq ($(SKIP_STACK_VALIDATION),1)
@@ -259,6 +274,7 @@ define rule_cc_o_c
$(call cmd,objtool)
$(call cmd,modversions_c)
$(call cmd,record_mcount)
+ $(call cmd,funcprototype)
endef
define rule_as_o_S
@@ -276,7 +292,7 @@ cmd_undef_syms = echo
endif
# Built-in and composite module parts
-$(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE
+$(obj)/%.o: $(src)/%.c $(recordmcount_source) $(funcprototype_source) $(objtool_dep) FORCE
$(call cmd,force_checksrc)
$(call if_changed_rule,cc_o_c)
diff --git a/scripts/ftrace/.gitignore b/scripts/ftrace/.gitignore
index 54d582c8faad..92aa4f335656 100644
--- a/scripts/ftrace/.gitignore
+++ b/scripts/ftrace/.gitignore
@@ -2,3 +2,5 @@
# Generated files
#
recordmcount
+funcprototype
+
diff --git a/scripts/ftrace/Makefile b/scripts/ftrace/Makefile
index 6797e51473e5..c44d131b075c 100644
--- a/scripts/ftrace/Makefile
+++ b/scripts/ftrace/Makefile
@@ -1,4 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
-hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
+hostprogs-$(BUILD_C_RECORDMCOUNT) := recordmcount
+
+hostprogs-$(CONFIG_FTRACE_FUNC_PROTOTYPE) += funcprototype
+HOSTLDLIBS_funcprototype += -lelf
+HOSTLDLIBS_funcprototype += -ldw
+
always := $(hostprogs-y)
diff --git a/scripts/ftrace/funcprototype.c b/scripts/ftrace/funcprototype.c
new file mode 100644
index 000000000000..064724047b19
--- /dev/null
+++ b/scripts/ftrace/funcprototype.c
@@ -0,0 +1,576 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * funcprototype.c: generate function prototypes of the locations of calls to
+ * 'mcount' so that ftrace can record function parameters and return value.
+ *
+ * Copyright 2019 Changbin Du <changbin.du@gmail.com>. All rights reserved.
+ *
+ * Usage: funcprototype [OPTION...] elf-file
+ *
+ * Here is an example for function memremap() in kernel/iomem.o. The
+ * declaration is:
+ * void *memremap(resource_size_t offset, size_t size, unsigned long flags)
+ *
+ * The output of funcprototype tool is:
+ * .section __funcprotostr, "a"
+ * .P_memremap_0:
+ * .string "offset"
+ * .P_memremap_1:
+ * .string "size"
+ * .P_memremap_2:
+ * .string "flags"
+ *
+ * .section __funcproto, "a"
+ * .quad memremap
+ * .byte 0x8
+ * .byte 0x3
+ * .quad .P_memremap_0
+ * .byte 0x8
+ * .byte 0x55
+ * .byte 0x0
+ * .quad .P_memremap_1
+ * .byte 0x8
+ * .byte 0x54
+ * .byte 0x0
+ * .quad .P_memremap_2
+ * .byte 0x8
+ * .byte 0x51
+ * .byte 0x0
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <argp.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <dwarf.h>
+#include <elfutils/libdw.h>
+#include <elfutils/libdwfl.h>
+
+struct func_param {
+ char *name;
+ uint8_t type;
+ u_int8_t loc[2]; /* Location expression, loc[0] is opcode */
+};
+
+struct func_prototype {
+ struct func_prototype *next;
+ bool skip;
+
+ char *name;
+ uint8_t ret_type;
+ uint8_t nr_param;
+ struct func_param *params;
+};
+
+#define MK_TYPE(sign, size) (((!!sign) << 7) | size)
+
+static bool is_64bit_obj;
+static struct func_prototype *func_prototype_list;
+
+
+static struct func_prototype *func_prototype_list_add_new(const char *name)
+{
+ struct func_prototype *proto;
+
+ proto = malloc(sizeof(*proto));
+ if (!proto)
+ errx(1, "no memory");
+ memset(proto, 0, sizeof(*proto));
+
+ proto->name = strdup(name);
+ if (!proto->name)
+ errx(1, "no memory");
+
+ if (!func_prototype_list) {
+ proto->next = NULL;
+ func_prototype_list = proto;
+ } else {
+ proto->next = func_prototype_list->next;
+ func_prototype_list->next = proto;
+ }
+
+ return proto;
+}
+
+static struct func_prototype *func_prototype_list_search(const char *name)
+{
+ struct func_prototype *proto;
+
+ for (proto = func_prototype_list; proto != NULL; proto = proto->next) {
+ if (!strcmp(proto->name, name))
+ return proto;
+ };
+ return NULL;
+}
+
+static void func_prototype_list_dumpnames(void)
+{
+ struct func_prototype *proto;
+
+ for (proto = func_prototype_list; proto != NULL; proto = proto->next)
+ printf("%s\n", proto->name);
+}
+
+static void func_prototype_list_destroy(void)
+{
+ struct func_prototype *proto;
+ int i;
+
+ while (func_prototype_list) {
+ proto = func_prototype_list;
+ func_prototype_list = func_prototype_list->next;
+
+ free(proto->name);
+ if (proto->params) {
+ for (i = 0; i < proto->nr_param; i++)
+ free(proto->params[i].name);
+ free(proto->params);
+ }
+ free(proto);
+ }
+}
+
+static bool is_mcount(char *name)
+{
+ return !strcmp(name, "__fentry__") ||
+ !strcmp(name, "_mcount") ||
+ !strcmp(name, "mcount");
+}
+
+static void check_elf(Elf *elf)
+{
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr;
+
+ ehdr = gelf_getehdr(elf, &ehdr_mem);
+ if (!ehdr)
+ errx(1, "cannot read ELF header");
+
+ is_64bit_obj = gelf_getclass(elf) == ELFCLASS64;
+
+ switch (ehdr->e_machine) {
+ case EM_386:
+ case EM_X86_64:
+ break;
+ default:
+ errx(1, "unsupported arch %d", ehdr->e_machine);
+ }
+}
+
+/**
+ * Search the symbole table to get the entry which matches @scn and @offset
+ * from relocation talbe.
+ */
+static char *search_mcount_caller(Elf *elf, GElf_Shdr *symshdr,
+ Elf_Data *symdata, int scn, int offset)
+{
+ int ndx;
+ char *caller;
+
+ for (ndx = 0; ndx < symshdr->sh_size / symshdr->sh_entsize; ++ndx) {
+ GElf_Sym sym;
+
+ gelf_getsym(symdata, ndx, &sym);
+
+ /* TODO: add local symobl support. */
+ if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL &&
+ scn == sym.st_shndx && (offset >= sym.st_value) &&
+ (offset < sym.st_value + sym.st_size)) {
+ caller = elf_strptr(elf, symshdr->sh_link, sym.st_name);
+ return caller;
+ }
+ }
+
+ return NULL;
+}
+
+/* Get all functions that call to mcount. */
+static void get_mcount_callers(const char *elf_file)
+{
+ Elf *elf;
+ Elf_Scn *scn = NULL;
+ GElf_Shdr shdr;
+ int fd;
+ int ndx;
+
+ fd = open(elf_file, O_RDONLY);
+ if (fd < 0)
+ errx(1, "can not open %s", elf_file);
+
+ elf_version(EV_CURRENT);
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+
+ check_elf(elf);
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ gelf_getshdr(scn, &shdr);
+
+ if (shdr.sh_type == SHT_REL || shdr.sh_type == SHT_RELA) {
+ Elf_Data *data = elf_getdata(scn, NULL);
+ Elf_Scn *symscn = elf_getscn(elf, shdr.sh_link);
+ Elf_Data *symdata = elf_getdata(symscn, NULL);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr(symscn, &symshdr_mem);
+
+ for (ndx = 0; ndx < shdr.sh_size / shdr.sh_entsize;
+ ++ndx) {
+ unsigned long sym_index;
+ unsigned long offset;
+ GElf_Sym sym;
+ char *symname;
+
+ if (shdr.sh_type == SHT_REL) {
+ GElf_Rel rel_mem;
+ GElf_Rel *rel = gelf_getrel(data, ndx,
+ &rel_mem);
+ sym_index = GELF_R_SYM(rel->r_info);
+ offset = rel->r_offset;
+ } else {
+ GElf_Rela rela_mem;
+ GElf_Rela *rela = gelf_getrela(
+ data, ndx, &rela_mem);
+ sym_index = GELF_R_SYM(rela->r_info);
+ offset = rela->r_offset;
+ }
+
+ gelf_getsym(symdata, sym_index, &sym);
+ symname = elf_strptr(elf, symshdr->sh_link,
+ sym.st_name);
+
+ if (is_mcount(symname)) {
+ const char *caller;
+
+ caller = search_mcount_caller(
+ elf, symshdr, symdata,
+ shdr.sh_info, offset);
+ if (caller)
+ func_prototype_list_add_new(caller);
+ }
+ }
+ }
+ }
+
+ elf_end(elf);
+ close(fd);
+}
+
+/*
+ * Get a variable size and sign info.
+ * TODO: Determine the expected display format. (e.g. size_t for "%lu").
+ */
+static void die_type_sign_bytes(Dwarf_Die *die, bool *is_signed, int *bytes)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Die type;
+ int ret;
+
+ *bytes = 0;
+ *is_signed = false;
+
+ ret = dwarf_peel_type(dwarf_formref_die(
+ dwarf_attr_integrate(die, DW_AT_type, &attr), &type),
+ &type);
+ if (ret == 0) {
+ Dwarf_Word val;
+
+ ret = dwarf_formudata(dwarf_attr(&type, DW_AT_encoding,
+ &attr), &val);
+ if (ret == 0)
+ *is_signed = (val == DW_ATE_signed) ||
+ (val == DW_ATE_signed_char);
+
+ if (dwarf_aggregate_size(&type, &val) == 0)
+ *bytes = val;
+ }
+}
+
+static int get_func_nr_params(Dwarf_Die *funcdie)
+{
+ Dwarf_Die child;
+ int count = 0;
+
+ if (dwarf_child(funcdie, &child) == 0) {
+ do {
+ if (dwarf_tag(&child) == DW_TAG_formal_parameter)
+ count++;
+ } while (dwarf_siblingof(&child, &child) == 0);
+ }
+
+ return count;
+}
+
+static int get_loc_expr(const char *fname, Dwarf_Op *loc, uint8_t expr[2])
+{
+ int ret = 0;
+ int off;
+
+ switch (loc->atom) {
+ case DW_OP_reg0 ... DW_OP_reg31:
+ expr[0] = loc->atom;
+ expr[1] = 0;
+ break;
+ case DW_OP_fbreg:
+ off = (int32_t)loc->number;
+
+ /*
+ * Very few functions have number that exceeds
+ * (SCHAR_MIN, SCHAR_MAX). We skip these
+ * functions to keep protrotype data as small
+ * as possilbe.
+ */
+ if (off > SCHAR_MAX || off < SCHAR_MIN) {
+ warnx("%s: loc fbreg offset %d too large", fname, off);
+ ret = -1;
+ } else {
+ expr[0] = loc->atom;
+ expr[1] = off; /* The operand is signed */
+ }
+ break;
+ case DW_OP_breg0 ... DW_OP_breg31:
+ off = (int32_t)loc->number;
+
+ if (off > SCHAR_MAX || off < SCHAR_MIN) {
+ warnx("%s: loc bregx offset %d too large", fname, off);
+ ret = -1;
+ } else {
+ expr[0] = loc->atom;
+ expr[1] = off;
+ }
+ break;
+ default:
+ warnx("%s: unsupported loc operation 0x%x",
+ fname, loc->atom);
+ ret = -1;
+ };
+
+ return ret;
+}
+
+static int handle_function(Dwarf_Die *funcdie, void *arg)
+{
+ const char *name = dwarf_diename(funcdie);
+ Dwarf_Addr func_addr;
+ Dwarf_Die child;
+ struct func_prototype *proto;
+ int nr_params;
+ int sz, n = 0;
+
+ if (!dwarf_hasattr(funcdie, DW_AT_low_pc))
+ return 0;
+
+ /* Such symbol is a local function generated by GCC ipa-fnsplit. */
+ if (!dwarf_hasattr(funcdie, DW_AT_name))
+ return 0;
+
+ /* check whether it is a mcount caller. */
+ proto = func_prototype_list_search(name);
+ if (!proto)
+ return 0;
+
+ nr_params = get_func_nr_params(funcdie);
+ sz = sizeof(proto->params[0]) * nr_params;
+ proto->params = malloc(sz);
+ if (!proto->params)
+ errx(1, "no memory");
+
+ memset(proto->params, 0, sz);
+
+ dwarf_lowpc(funcdie, &func_addr);
+
+ /* get function return type */
+ if (dwarf_hasattr(funcdie, DW_AT_type)) {
+ bool is_signed;
+ int bytes;
+
+ die_type_sign_bytes(funcdie, &is_signed, &bytes);
+ proto->ret_type = MK_TYPE(is_signed, bytes);
+ } else
+ proto->ret_type = 0;
+
+ /* process function parameters. */
+ if (dwarf_child(funcdie, &child) == 0) {
+ do {
+ if (dwarf_tag(&child) == DW_TAG_formal_parameter) {
+ Dwarf_Attribute locattr;
+ Dwarf_Op *loc;
+ size_t nloc = 0;
+ bool is_signed;
+ int bytes;
+
+ die_type_sign_bytes(&child, &is_signed, &bytes);
+ proto->params[n].name = strdup(dwarf_diename(&child));
+ proto->params[n].type = MK_TYPE(is_signed, bytes);
+
+ if (!dwarf_hasattr(&child, DW_AT_location))
+ errx(1, "%s: no location attr", name);
+
+ dwarf_attr(&child, DW_AT_location, &locattr);
+ if (dwarf_getlocation(&locattr, &loc, &nloc) < 0) {
+ Dwarf_Addr base, begin, end;
+
+ if (dwarf_getlocations(
+ &locattr, 0, &base,
+ &begin, &end, &loc,
+ &nloc) <= 0)
+ errx(1, "%s: no param loc info",
+ name);
+ }
+ if (get_loc_expr(name, loc, proto->params[n].loc)) {
+ /* skip this function. */
+ proto->skip = true;
+ return 0;
+ }
+
+ n++;
+ };
+ } while (dwarf_siblingof(&child, &child) == 0);
+ }
+
+ proto->nr_param = n;
+ return 0;
+}
+
+static const Dwfl_Callbacks offline_callbacks = {
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .section_address = dwfl_offline_section_address,
+};
+
+/* Iterate each DW_TAG_subprogram DIE to get their prototype info. */
+static void dwarf_get_prototypes(const char *elf_file)
+{
+ Dwfl *dwfl = NULL;
+ Dwarf_Die *cu = NULL;
+ Dwarf_Addr dwbias;
+ int ret;
+
+ dwfl = dwfl_begin(&offline_callbacks);
+ if (dwfl == NULL)
+ errx(1, "dwfl fail");
+
+ if (dwfl_report_offline(dwfl, "", elf_file, -1) == NULL)
+ errx(1, "dwfl report fail");
+
+ ret = dwfl_report_end(dwfl, NULL, NULL);
+ assert(ret == 0);
+
+ while ((cu = dwfl_nextcu(dwfl, cu, &dwbias)) != NULL)
+ dwarf_getfuncs(cu, &handle_function, NULL, 0);
+}
+
+static void print_prototypes_assembly(void)
+{
+ struct func_prototype *proto;
+ int i;
+
+ if (!func_prototype_list)
+ return;
+
+ printf(" .section __funcprotostr, \"a\"\n");
+ for (proto = func_prototype_list; proto != NULL; proto = proto->next) {
+ if (proto->skip)
+ continue;
+ for (i = 0; i < proto->nr_param; i++) {
+ printf(".P_%s_%d:\n", proto->name, i);
+ printf(" .string \"%s\"\n", proto->params[i].name);
+ }
+ };
+
+ printf("\n .section __funcproto, \"a\"\n");
+ for (proto = func_prototype_list; proto != NULL; proto = proto->next) {
+ if (proto->skip)
+ continue;
+ if (is_64bit_obj)
+ printf(" .quad %s\n", proto->name);
+ else
+ printf(" .long %s\n", proto->name);
+ printf(" .byte 0x%x\n", proto->ret_type);
+ printf(" .byte 0x%x\n", proto->nr_param);
+ for (i = 0; i < proto->nr_param; i++) {
+ if (is_64bit_obj)
+ printf(" .quad .P_%s_%d\n", proto->name, i);
+ else
+ printf(" .long .P_%s_%d\n", proto->name, i);
+ printf(" .byte 0x%x\n", proto->params[i].type);
+ printf(" .byte 0x%x\n", proto->params[i].loc[0]);
+ printf(" .byte 0x%x\n", proto->params[i].loc[1]);
+ }
+ printf("\n");
+ };
+}
+
+/* Program documentation. */
+static char doc[] =
+ "funcprototype -- a program to generate mcount caller prototypes";
+
+/* A description of the arguments we accept. */
+static const char args_doc[] = "elf-file";
+
+/* The options we understand. */
+static struct argp_option options[] = { { "mcount-callers", 'm', 0, 0,
+ "show mcount callers only" },
+ { 0 } };
+
+struct arguments {
+ char *elf_file;
+ int show_callers_only;
+};
+
+/* Parse options. */
+static error_t parse_opt(int key, char *arg, struct argp_state *state)
+{
+ struct arguments *arguments = state->input;
+
+ switch (key) {
+ case 'm':
+ arguments->show_callers_only = 1;
+ break;
+ case ARGP_KEY_ARG:
+ if (state->arg_num > 2) {
+ /* Too many arguments. */
+ argp_usage(state);
+ }
+ arguments->elf_file = arg;
+ break;
+ case ARGP_KEY_END:
+ if (state->arg_num < 1)
+ /* Not enough arguments. */
+ argp_usage(state);
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Our argp parser. */
+static struct argp argp = { options, parse_opt, args_doc, doc };
+
+int main(int argc, char *argv[])
+{
+ struct arguments arguments;
+
+ arguments.show_callers_only = 0;
+ argp_parse(&argp, argc, argv, 0, 0, &arguments);
+
+ get_mcount_callers(arguments.elf_file);
+
+ if (arguments.show_callers_only) {
+ func_prototype_list_dumpnames();
+ goto free;
+ }
+
+ dwarf_get_prototypes(arguments.elf_file);
+ print_prototypes_assembly();
+
+free:
+ func_prototype_list_destroy();
+ return 0;
+}
--
2.20.1
^ permalink raw reply related
* [PATCH 03/11] asm-generic: add generic dwarf definition
From: Changbin Du @ 2019-08-25 13:23 UTC (permalink / raw)
To: Steven Rostedt, Ingo Molnar
Cc: Jonathan Corbet, Jessica Yu, Thomas Gleixner, x86, linux-doc,
linux-kernel, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
linux-arch, linux-kbuild, Changbin Du
In-Reply-To: <20190825132330.5015-1-changbin.du@gmail.com>
Add generic DWARF constant definitions. We will use it later.
Signed-off-by: Changbin Du <changbin.du@gmail.com>
---
include/asm-generic/dwarf.h | 199 ++++++++++++++++++++++++++++++++++++
1 file changed, 199 insertions(+)
create mode 100644 include/asm-generic/dwarf.h
diff --git a/include/asm-generic/dwarf.h b/include/asm-generic/dwarf.h
new file mode 100644
index 000000000000..c705633c2a8f
--- /dev/null
+++ b/include/asm-generic/dwarf.h
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Architecture independent definitions of DWARF.
+ *
+ * Copyright (C) 2019 Changbin Du <changbin.du@gmail.com>
+ */
+#ifndef __ASM_GENERIC_DWARF_H
+#define __ASM_GENERIC_DWARF_H
+
+/*
+ * DWARF expression operations
+ */
+#define DW_OP_addr 0x03
+#define DW_OP_deref 0x06
+#define DW_OP_const1u 0x08
+#define DW_OP_const1s 0x09
+#define DW_OP_const2u 0x0a
+#define DW_OP_const2s 0x0b
+#define DW_OP_const4u 0x0c
+#define DW_OP_const4s 0x0d
+#define DW_OP_const8u 0x0e
+#define DW_OP_const8s 0x0f
+#define DW_OP_constu 0x10
+#define DW_OP_consts 0x11
+#define DW_OP_dup 0x12
+#define DW_OP_drop 0x13
+#define DW_OP_over 0x14
+#define DW_OP_pick 0x15
+#define DW_OP_swap 0x16
+#define DW_OP_rot 0x17
+#define DW_OP_xderef 0x18
+#define DW_OP_abs 0x19
+#define DW_OP_and 0x1a
+#define DW_OP_div 0x1b
+#define DW_OP_minus 0x1c
+#define DW_OP_mod 0x1d
+#define DW_OP_mul 0x1e
+#define DW_OP_neg 0x1f
+#define DW_OP_not 0x20
+#define DW_OP_or 0x21
+#define DW_OP_plus 0x22
+#define DW_OP_plus_uconst 0x23
+#define DW_OP_shl 0x24
+#define DW_OP_shr 0x25
+#define DW_OP_shra 0x26
+#define DW_OP_xor 0x27
+#define DW_OP_skip 0x2f
+#define DW_OP_bra 0x28
+#define DW_OP_eq 0x29
+#define DW_OP_ge 0x2a
+#define DW_OP_gt 0x2b
+#define DW_OP_le 0x2c
+#define DW_OP_lt 0x2d
+#define DW_OP_ne 0x2e
+#define DW_OP_lit0 0x30
+#define DW_OP_lit1 0x31
+#define DW_OP_lit2 0x32
+#define DW_OP_lit3 0x33
+#define DW_OP_lit4 0x34
+#define DW_OP_lit5 0x35
+#define DW_OP_lit6 0x36
+#define DW_OP_lit7 0x37
+#define DW_OP_lit8 0x38
+#define DW_OP_lit9 0x39
+#define DW_OP_lit10 0x3a
+#define DW_OP_lit11 0x3b
+#define DW_OP_lit12 0x3c
+#define DW_OP_lit13 0x3d
+#define DW_OP_lit14 0x3e
+#define DW_OP_lit15 0x3f
+#define DW_OP_lit16 0x40
+#define DW_OP_lit17 0x41
+#define DW_OP_lit18 0x42
+#define DW_OP_lit19 0x43
+#define DW_OP_lit20 0x44
+#define DW_OP_lit21 0x45
+#define DW_OP_lit22 0x46
+#define DW_OP_lit23 0x47
+#define DW_OP_lit24 0x48
+#define DW_OP_lit25 0x49
+#define DW_OP_lit26 0x4a
+#define DW_OP_lit27 0x4b
+#define DW_OP_lit28 0x4c
+#define DW_OP_lit29 0x4d
+#define DW_OP_lit30 0x4e
+#define DW_OP_lit31 0x4f
+#define DW_OP_reg0 0x50
+#define DW_OP_reg1 0x51
+#define DW_OP_reg2 0x52
+#define DW_OP_reg3 0x53
+#define DW_OP_reg4 0x54
+#define DW_OP_reg5 0x55
+#define DW_OP_reg6 0x56
+#define DW_OP_reg7 0x57
+#define DW_OP_reg8 0x58
+#define DW_OP_reg9 0x59
+#define DW_OP_reg10 0x5a
+#define DW_OP_reg11 0x5b
+#define DW_OP_reg12 0x5c
+#define DW_OP_reg13 0x5d
+#define DW_OP_reg14 0x5e
+#define DW_OP_reg15 0x5f
+#define DW_OP_reg16 0x60
+#define DW_OP_reg17 0x61
+#define DW_OP_reg18 0x62
+#define DW_OP_reg19 0x63
+#define DW_OP_reg20 0x64
+#define DW_OP_reg21 0x65
+#define DW_OP_reg22 0x66
+#define DW_OP_reg23 0x67
+#define DW_OP_reg24 0x68
+#define DW_OP_reg25 0x69
+#define DW_OP_reg26 0x6a
+#define DW_OP_reg27 0x6b
+#define DW_OP_reg28 0x6c
+#define DW_OP_reg29 0x6d
+#define DW_OP_reg30 0x6e
+#define DW_OP_reg31 0x6f
+#define DW_OP_breg0 0x70
+#define DW_OP_breg1 0x71
+#define DW_OP_breg2 0x72
+#define DW_OP_breg3 0x73
+#define DW_OP_breg4 0x74
+#define DW_OP_breg5 0x75
+#define DW_OP_breg6 0x76
+#define DW_OP_breg7 0x77
+#define DW_OP_breg8 0x78
+#define DW_OP_breg9 0x79
+#define DW_OP_breg10 0x7a
+#define DW_OP_breg11 0x7b
+#define DW_OP_breg12 0x7c
+#define DW_OP_breg13 0x7d
+#define DW_OP_breg14 0x7e
+#define DW_OP_breg15 0x7f
+#define DW_OP_breg16 0x80
+#define DW_OP_breg17 0x81
+#define DW_OP_breg18 0x82
+#define DW_OP_breg19 0x83
+#define DW_OP_breg20 0x84
+#define DW_OP_breg21 0x85
+#define DW_OP_breg22 0x86
+#define DW_OP_breg23 0x87
+#define DW_OP_breg24 0x88
+#define DW_OP_breg25 0x89
+#define DW_OP_breg26 0x8a
+#define DW_OP_breg27 0x8b
+#define DW_OP_breg28 0x8c
+#define DW_OP_breg29 0x8d
+#define DW_OP_breg30 0x8e
+#define DW_OP_breg31 0x8f
+#define DW_OP_regx 0x90
+#define DW_OP_fbreg 0x91
+#define DW_OP_bregx 0x92
+#define DW_OP_piece 0x93
+#define DW_OP_deref_size 0x94
+#define DW_OP_xderef_size 0x95
+#define DW_OP_nop 0x96
+#define DW_OP_push_object_address 0x97
+#define DW_OP_call2 0x98
+#define DW_OP_call4 0x99
+#define DW_OP_call_ref 0x9a
+#define DW_OP_form_tls_address 0x9b
+#define DW_OP_call_frame_cfa 0x9c
+#define DW_OP_bit_piece 0x9d
+#define DW_OP_implicit_value 0x9e
+#define DW_OP_stack_value 0x9f
+#define DW_OP_implicit_pointer 0xa0
+#define DW_OP_addrx 0xa1
+#define DW_OP_constx 0xa2
+#define DW_OP_entry_value 0xa3
+#define DW_OP_const_type 0xa4
+#define DW_OP_regval_type 0xa5
+#define DW_OP_deref_type 0xa6
+#define DW_OP_xderef_type 0xa7
+#define DW_OP_convert 0xa8
+#define DW_OP_reinterpret 0xa9
+
+/* GNU extensions. */
+#define DW_OP_GNU_push_tls_address 0xe0
+#define DW_OP_GNU_uninit 0xf0
+#define DW_OP_GNU_encoded_addr 0xf1
+#define DW_OP_GNU_implicit_pointer 0xf2
+#define DW_OP_GNU_entry_value 0xf3
+#define DW_OP_GNU_const_type 0xf4
+#define DW_OP_GNU_regval_type 0xf5
+#define DW_OP_GNU_deref_type 0xf6
+#define DW_OP_GNU_convert 0xf7
+#define DW_OP_GNU_reinterpret 0xf9
+#define DW_OP_GNU_parameter_ref 0xfa
+
+/* GNU Debug Fission extensions. */
+#define DW_OP_GNU_addr_index 0xfb,
+#define DW_OP_GNU_const_index 0xfc
+#define DW_OP_GNU_variable_value 0xfd
+
+#define DW_OP_lo_user 0xe0
+#define DW_OP_hi_user 0xff
+
+#endif /* __ASM_GENERIC_DWARF_H */
--
2.20.1
^ permalink raw reply related
* [PATCH 04/11] ftrace/hash: add private data field
From: Changbin Du @ 2019-08-25 13:23 UTC (permalink / raw)
To: Steven Rostedt, Ingo Molnar
Cc: Jonathan Corbet, Jessica Yu, Thomas Gleixner, x86, linux-doc,
linux-kernel, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
linux-arch, linux-kbuild, Changbin Du
In-Reply-To: <20190825132330.5015-1-changbin.du@gmail.com>
We will reuse ftrace_hash to lookup function prototype information. So
we need an additional field to bind ftrace_func_entry to prototype
information.
Signed-off-by: Changbin Du <changbin.du@gmail.com>
---
kernel/trace/ftrace.c | 17 +++++++----------
kernel/trace/trace.h | 6 ++++++
2 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index eca34503f178..a314f0768b2c 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1017,11 +1017,6 @@ static bool update_all_ops;
# error Dynamic ftrace depends on MCOUNT_RECORD
#endif
-struct ftrace_func_entry {
- struct hlist_node hlist;
- unsigned long ip;
-};
-
struct ftrace_func_probe {
struct ftrace_probe_ops *probe_ops;
struct ftrace_ops ops;
@@ -1169,7 +1164,8 @@ static void __add_hash_entry(struct ftrace_hash *hash,
hash->count++;
}
-static int add_hash_entry(struct ftrace_hash *hash, unsigned long ip)
+static int add_hash_entry(struct ftrace_hash *hash, unsigned long ip,
+ void *priv)
{
struct ftrace_func_entry *entry;
@@ -1178,6 +1174,7 @@ static int add_hash_entry(struct ftrace_hash *hash, unsigned long ip)
return -ENOMEM;
entry->ip = ip;
+ entry->priv = priv;
__add_hash_entry(hash, entry);
return 0;
@@ -1346,7 +1343,7 @@ alloc_and_copy_ftrace_hash(int size_bits, struct ftrace_hash *hash)
size = 1 << hash->size_bits;
for (i = 0; i < size; i++) {
hlist_for_each_entry(entry, &hash->buckets[i], hlist) {
- ret = add_hash_entry(new_hash, entry->ip);
+ ret = add_hash_entry(new_hash, entry->ip, NULL);
if (ret < 0)
goto free_hash;
}
@@ -3694,7 +3691,7 @@ enter_record(struct ftrace_hash *hash, struct dyn_ftrace *rec, int clear_filter)
if (entry)
return 0;
- ret = add_hash_entry(hash, rec->ip);
+ ret = add_hash_entry(hash, rec->ip, NULL);
}
return ret;
}
@@ -4700,7 +4697,7 @@ ftrace_match_addr(struct ftrace_hash *hash, unsigned long ip, int remove)
return 0;
}
- return add_hash_entry(hash, ip);
+ return add_hash_entry(hash, ip, NULL);
}
static int
@@ -5380,7 +5377,7 @@ ftrace_graph_set_hash(struct ftrace_hash *hash, char *buffer)
if (entry)
continue;
- if (add_hash_entry(hash, rec->ip) < 0)
+ if (add_hash_entry(hash, rec->ip, NULL) < 0)
goto out;
} else {
if (entry) {
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 005f08629b8b..ad619c73a505 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -882,6 +882,12 @@ struct ftrace_hash {
struct rcu_head rcu;
};
+struct ftrace_func_entry {
+ struct hlist_node hlist;
+ unsigned long ip;
+ void *priv;
+};
+
struct ftrace_func_entry *
ftrace_lookup_ip(struct ftrace_hash *hash, unsigned long ip);
--
2.20.1
^ permalink raw reply related
* [PATCH 05/11] ftrace: create memcache for hash entries
From: Changbin Du @ 2019-08-25 13:23 UTC (permalink / raw)
To: Steven Rostedt, Ingo Molnar
Cc: Jonathan Corbet, Jessica Yu, Thomas Gleixner, x86, linux-doc,
linux-kernel, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
linux-arch, linux-kbuild, Changbin Du
In-Reply-To: <20190825132330.5015-1-changbin.du@gmail.com>
When CONFIG_FTRACE_FUNC_PROTOTYPE is enabled, thousands of
ftrace_func_entry instances are created. So create a dedicated
memcache to enhance performance.
Signed-off-by: Changbin Du <changbin.du@gmail.com>
---
kernel/trace/ftrace.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index a314f0768b2c..cfcb8dad93ea 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -94,6 +94,8 @@ struct ftrace_ops *function_trace_op __read_mostly = &ftrace_list_end;
/* What to set function_trace_op to */
static struct ftrace_ops *set_function_trace_op;
+struct kmem_cache *hash_entry_cache;
+
static bool ftrace_pids_enabled(struct ftrace_ops *ops)
{
struct trace_array *tr;
@@ -1169,7 +1171,7 @@ static int add_hash_entry(struct ftrace_hash *hash, unsigned long ip,
{
struct ftrace_func_entry *entry;
- entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ entry = kmem_cache_alloc(hash_entry_cache, GFP_KERNEL);
if (!entry)
return -ENOMEM;
@@ -6153,6 +6155,15 @@ void __init ftrace_init(void)
if (ret)
goto failed;
+ hash_entry_cache = kmem_cache_create("ftrace-hash",
+ sizeof(struct ftrace_func_entry),
+ sizeof(struct ftrace_func_entry),
+ 0, NULL);
+ if (!hash_entry_cache) {
+ pr_err("failed to create ftrace hash entry cache\n");
+ goto failed;
+ }
+
count = __stop_mcount_loc - __start_mcount_loc;
if (!count) {
pr_info("ftrace: No functions to be traced?\n");
@@ -6172,6 +6183,10 @@ void __init ftrace_init(void)
return;
failed:
+ if (hash_entry_cache) {
+ kmem_cache_destroy(hash_entry_cache);
+ hash_entry_cache = NULL;
+ }
ftrace_disabled = 1;
}
--
2.20.1
^ permalink raw reply related
* [PATCH 06/11] ftrace: process function prototype data in vmlinux and modules
From: Changbin Du @ 2019-08-25 13:23 UTC (permalink / raw)
To: Steven Rostedt, Ingo Molnar
Cc: Jonathan Corbet, Jessica Yu, Thomas Gleixner, x86, linux-doc,
linux-kernel, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
linux-arch, linux-kbuild, Changbin Du
In-Reply-To: <20190825132330.5015-1-changbin.du@gmail.com>
Walk through the '__funcproto' section in vmlinux and kernel modules.
For each item we add it to a new ftrace hash table ftrace_prototype_hash.
When unloading a module, its items are removed from hash table.
Signed-off-by: Changbin Du <changbin.du@gmail.com>
---
include/asm-generic/vmlinux.lds.h | 18 ++++++++
include/linux/ftrace.h | 18 ++++++++
include/linux/module.h | 4 ++
kernel/module.c | 25 ++++++++--
kernel/trace/ftrace.c | 76 ++++++++++++++++++++++++++++++-
kernel/trace/trace.h | 4 ++
6 files changed, 140 insertions(+), 5 deletions(-)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index cd28f63bfbc7..3b0a10cbf0ca 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -125,6 +125,23 @@
#define MCOUNT_REC()
#endif
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+#define FUNC_PROTOTYPE \
+ . = ALIGN(8); \
+ __funcprotostr : AT(ADDR(__funcprotostr) - LOAD_OFFSET) { \
+ KEEP(*(__funcprotostr)) \
+ } \
+ \
+ . = ALIGN(8); \
+ __funcproto : AT(ADDR(__funcproto) - LOAD_OFFSET) { \
+ __start_funcproto = .; \
+ KEEP(*(__funcproto)) \
+ __stop_funcproto = .; \
+ }
+#else
+#define FUNC_PROTOTYPE
+#endif
+
#ifdef CONFIG_TRACE_BRANCH_PROFILING
#define LIKELY_PROFILE() __start_annotated_branch_profile = .; \
KEEP(*(_ftrace_annotated_branch)) \
@@ -396,6 +413,7 @@
} \
\
TRACEDATA \
+ FUNC_PROTOTYPE \
\
/* Kernel symbol table: Normal symbols */ \
__ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 8a8cb3c401b2..f5aab37a8c34 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -361,6 +361,24 @@ struct dyn_ftrace {
struct dyn_arch_ftrace arch;
};
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+struct func_param {
+ char *name;
+ uint8_t type;
+ uint8_t loc[2];
+} __packed;
+
+struct func_prototype {
+ unsigned long ip;
+ uint8_t ret_type;
+ uint8_t nr_param;
+ struct func_param params[0];
+} __packed;
+
+#define FTRACE_PROTOTYPE_SIGNED(t) (t & BIT(7))
+#define FTRACE_PROTOTYPE_SIZE(t) (t & GENMASK(6, 0))
+#endif
+
int ftrace_force_update(void);
int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip,
int remove, int reset);
diff --git a/include/linux/module.h b/include/linux/module.h
index 1455812dd325..516062dfe567 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -477,6 +477,10 @@ struct module {
unsigned int num_ftrace_callsites;
unsigned long *ftrace_callsites;
#endif
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ struct func_prototype *funcproto_start;
+ size_t funcproto_sec_size;
+#endif
#ifdef CONFIG_LIVEPATCH
bool klp; /* Is this a livepatch module? */
diff --git a/kernel/module.c b/kernel/module.c
index 9ee93421269c..1c5eea7b6a28 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -360,17 +360,30 @@ static void *section_addr(const struct load_info *info, const char *name)
return (void *)info->sechdrs[find_sec(info, name)].sh_addr;
}
+/* Get info of a module section. */
+static void *section_info(const struct load_info *info,
+ const char *name,
+ size_t *size)
+{
+ unsigned int sec = find_sec(info, name);
+
+ /* Section 0 has sh_addr 0 and sh_size 0. */
+ *size = info->sechdrs[sec].sh_size;
+ return (void *)info->sechdrs[sec].sh_addr;
+}
+
/* Find a module section, or NULL. Fill in number of "objects" in section. */
static void *section_objs(const struct load_info *info,
const char *name,
size_t object_size,
unsigned int *num)
{
- unsigned int sec = find_sec(info, name);
+ void *addr;
+ size_t sz;
- /* Section 0 has sh_addr 0 and sh_size 0. */
- *num = info->sechdrs[sec].sh_size / object_size;
- return (void *)info->sechdrs[sec].sh_addr;
+ addr = section_info(info, name, &sz);
+ *num = sz / object_size;
+ return addr;
}
/* Provided by the linker */
@@ -3140,6 +3153,10 @@ static int find_module_sections(struct module *mod, struct load_info *info)
sizeof(*mod->ftrace_callsites),
&mod->num_ftrace_callsites);
#endif
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ mod->funcproto_start = section_info(info, "__funcproto",
+ &mod->funcproto_sec_size);
+#endif
#ifdef CONFIG_FUNCTION_ERROR_INJECTION
mod->ei_funcs = section_objs(info, "_error_injection_whitelist",
sizeof(*mod->ei_funcs),
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index cfcb8dad93ea..438b8b47198f 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5060,6 +5060,9 @@ static DEFINE_MUTEX(graph_lock);
struct ftrace_hash *ftrace_graph_hash = EMPTY_HASH;
struct ftrace_hash *ftrace_graph_notrace_hash = EMPTY_HASH;
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+struct ftrace_hash *ftrace_prototype_hash = EMPTY_HASH;
+#endif
enum graph_filter_type {
GRAPH_FILTER_NOTRACE = 0,
@@ -5615,6 +5618,46 @@ static int ftrace_process_locs(struct module *mod,
return ret;
}
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+static int ftrace_process_funcproto(struct module *mod,
+ struct func_prototype *start,
+ struct func_prototype *end,
+ bool remove)
+{
+ struct ftrace_func_entry *ent;
+ struct func_prototype *proto;
+ int ret = 0;
+
+ mutex_lock(&ftrace_lock);
+
+restart:
+ proto = start;
+ while (proto < end) {
+ if (remove) {
+ ent = ftrace_lookup_ip(ftrace_prototype_hash,
+ proto->ip);
+ if (ent)
+ free_hash_entry(ftrace_prototype_hash, ent);
+ } else {
+ ret = add_hash_entry(ftrace_prototype_hash,
+ proto->ip, proto);
+ if (ret < 0) {
+ end = proto;
+ remove = 1;
+ goto restart;
+ }
+ }
+ proto = (struct func_prototype *)((char *)proto +
+ sizeof(*proto) +
+ sizeof(proto->params[0]) * proto->nr_param);
+ }
+
+ mutex_unlock(&ftrace_lock);
+
+ return ret;
+}
+#endif
+
struct ftrace_mod_func {
struct list_head list;
char *name;
@@ -5707,7 +5750,7 @@ static void ftrace_free_mod_map(struct rcu_head *rcu)
kfree(mod_map);
}
-void ftrace_release_mod(struct module *mod)
+void ftrace_release_dyn(struct module *mod)
{
struct ftrace_mod_map *mod_map;
struct ftrace_mod_map *n;
@@ -5773,6 +5816,17 @@ void ftrace_release_mod(struct module *mod)
}
}
+void ftrace_release_mod(struct module *mod)
+{
+ ftrace_release_dyn(mod);
+
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ ftrace_process_funcproto(mod, mod->funcproto_start,
+ (void *)mod->funcproto_start + mod->funcproto_sec_size,
+ true);
+#endif
+}
+
void ftrace_module_enable(struct module *mod)
{
struct dyn_ftrace *rec;
@@ -5852,6 +5906,11 @@ void ftrace_module_init(struct module *mod)
ftrace_process_locs(mod, mod->ftrace_callsites,
mod->ftrace_callsites + mod->num_ftrace_callsites);
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ ftrace_process_funcproto(mod, mod->funcproto_start,
+ (void *)mod->funcproto_start + mod->funcproto_sec_size,
+ false);
+#endif
}
static void save_ftrace_mod_rec(struct ftrace_mod_map *mod_map,
@@ -6146,6 +6205,10 @@ void __init ftrace_init(void)
{
extern unsigned long __start_mcount_loc[];
extern unsigned long __stop_mcount_loc[];
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ extern struct func_prototype __start_funcproto[];
+ extern struct func_prototype __stop_funcproto[];
+#endif
unsigned long count, flags;
int ret;
@@ -6179,6 +6242,17 @@ void __init ftrace_init(void)
__start_mcount_loc,
__stop_mcount_loc);
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ ftrace_prototype_hash = alloc_ftrace_hash(FTRACE_HASH_DEFAULT_BITS);
+ if (WARN_ON(!ftrace_prototype_hash))
+ goto failed;
+
+ ftrace_process_funcproto(NULL,
+ __start_funcproto,
+ __stop_funcproto,
+ false);
+#endif
+
set_ftrace_early_filters();
return;
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index ad619c73a505..22433a15e340 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -940,6 +940,10 @@ extern void __trace_graph_return(struct trace_array *tr,
extern struct ftrace_hash *ftrace_graph_hash;
extern struct ftrace_hash *ftrace_graph_notrace_hash;
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+extern struct ftrace_hash *ftrace_prototype_hash;
+#endif
+
static inline int ftrace_graph_addr(struct ftrace_graph_ent *trace)
{
unsigned long addr = trace->func;
--
2.20.1
^ permalink raw reply related
* [PATCH 07/11] ftrace: prepare arch specific interfaces for function prototype feature
From: Changbin Du @ 2019-08-25 13:23 UTC (permalink / raw)
To: Steven Rostedt, Ingo Molnar
Cc: Jonathan Corbet, Jessica Yu, Thomas Gleixner, x86, linux-doc,
linux-kernel, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
linux-arch, linux-kbuild, Changbin Du
In-Reply-To: <20190825132330.5015-1-changbin.du@gmail.com>
To record function parameter and return value, we need the arch specific
code to pass the saved register context. It is only valid if the
CONFIG_FTRACE_FUNC_PROTOTYPE feature is enabled. This patch only changes
the interfaces, real implementation will be added later.
Signed-off-by: Changbin Du <changbin.du@gmail.com>
---
arch/arm/kernel/ftrace.c | 2 +-
arch/arm64/kernel/ftrace.c | 2 +-
arch/csky/kernel/ftrace.c | 2 +-
arch/microblaze/kernel/ftrace.c | 2 +-
arch/mips/kernel/ftrace.c | 2 +-
arch/nds32/kernel/ftrace.c | 5 +++--
arch/parisc/kernel/ftrace.c | 2 +-
arch/powerpc/kernel/trace/ftrace.c | 2 +-
arch/riscv/kernel/ftrace.c | 2 +-
arch/s390/kernel/ftrace.c | 2 +-
arch/sh/kernel/ftrace.c | 2 +-
arch/sparc/kernel/ftrace.c | 2 +-
arch/x86/kernel/ftrace.c | 2 +-
include/linux/ftrace.h | 10 +++++++---
kernel/trace/fgraph.c | 21 +++++++++++++++------
kernel/trace/ftrace.c | 4 +++-
kernel/trace/trace.h | 2 +-
kernel/trace/trace_functions_graph.c | 2 +-
kernel/trace/trace_irqsoff.c | 3 ++-
kernel/trace/trace_sched_wakeup.c | 3 ++-
20 files changed, 46 insertions(+), 28 deletions(-)
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
index bda949fd84e8..fd01c08b2dcb 100644
--- a/arch/arm/kernel/ftrace.c
+++ b/arch/arm/kernel/ftrace.c
@@ -191,7 +191,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
old = *parent;
*parent = return_hooker;
- if (function_graph_enter(old, self_addr, frame_pointer, NULL))
+ if (function_graph_enter(old, self_addr, frame_pointer, NULL, NULL))
*parent = old;
}
diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
index 171773257974..dc8cc516c00a 100644
--- a/arch/arm64/kernel/ftrace.c
+++ b/arch/arm64/kernel/ftrace.c
@@ -233,7 +233,7 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
*/
old = *parent;
- if (!function_graph_enter(old, self_addr, frame_pointer, NULL))
+ if (!function_graph_enter(old, self_addr, frame_pointer, NULL, NULL))
*parent = return_hooker;
}
diff --git a/arch/csky/kernel/ftrace.c b/arch/csky/kernel/ftrace.c
index 44f4880179b7..5bc67f447e78 100644
--- a/arch/csky/kernel/ftrace.c
+++ b/arch/csky/kernel/ftrace.c
@@ -148,7 +148,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
old = *parent;
if (!function_graph_enter(old, self_addr,
- *(unsigned long *)frame_pointer, parent)) {
+ *(unsigned long *)frame_pointer, parent, NULL)) {
/*
* For csky-gcc function has sub-call:
* subi sp, sp, 8
diff --git a/arch/microblaze/kernel/ftrace.c b/arch/microblaze/kernel/ftrace.c
index 224eea40e1ee..9722e98cd01d 100644
--- a/arch/microblaze/kernel/ftrace.c
+++ b/arch/microblaze/kernel/ftrace.c
@@ -62,7 +62,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
return;
}
- if (function_graph_enter(old, self_addr, 0, NULL))
+ if (function_graph_enter(old, self_addr, 0, NULL, NULL))
*parent = old;
}
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
diff --git a/arch/mips/kernel/ftrace.c b/arch/mips/kernel/ftrace.c
index 2625232bfe52..24668bf079d2 100644
--- a/arch/mips/kernel/ftrace.c
+++ b/arch/mips/kernel/ftrace.c
@@ -378,7 +378,7 @@ void prepare_ftrace_return(unsigned long *parent_ra_addr, unsigned long self_ra,
insns = core_kernel_text(self_ra) ? 2 : MCOUNT_OFFSET_INSNS + 1;
self_ra -= (MCOUNT_INSN_SIZE * insns);
- if (function_graph_enter(old_parent_ra, self_ra, fp, NULL))
+ if (function_graph_enter(old_parent_ra, self_ra, fp, NULL, NULL))
*parent_ra_addr = old_parent_ra;
return;
out:
diff --git a/arch/nds32/kernel/ftrace.c b/arch/nds32/kernel/ftrace.c
index fd2a54b8cd57..3dbf0017dfdf 100644
--- a/arch/nds32/kernel/ftrace.c
+++ b/arch/nds32/kernel/ftrace.c
@@ -217,7 +217,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
old = *parent;
- if (!function_graph_enter(old, self_addr, frame_pointer, NULL))
+ if (!function_graph_enter(old, self_addr, frame_pointer, NULL, NULL))
*parent = return_hooker;
}
@@ -235,7 +235,8 @@ noinline void ftrace_graph_caller(void)
prepare_ftrace_return(parent_ip, selfpc, frame_pointer);
}
-extern unsigned long ftrace_return_to_handler(unsigned long frame_pointer);
+extern unsigned long ftrace_return_to_handler(unsigned long frame_pointer,
+ unsigned long retval);
void __naked return_to_handler(void)
{
__asm__ __volatile__ (
diff --git a/arch/parisc/kernel/ftrace.c b/arch/parisc/kernel/ftrace.c
index b6fb30f2e4bf..ea02f36e4f84 100644
--- a/arch/parisc/kernel/ftrace.c
+++ b/arch/parisc/kernel/ftrace.c
@@ -40,7 +40,7 @@ static void __hot prepare_ftrace_return(unsigned long *parent,
old = *parent;
- if (!function_graph_enter(old, self_addr, 0, NULL))
+ if (!function_graph_enter(old, self_addr, 0, NULL, NULL))
/* activate parisc_return_to_handler() as return point */
*parent = (unsigned long) &parisc_return_to_handler;
}
diff --git a/arch/powerpc/kernel/trace/ftrace.c b/arch/powerpc/kernel/trace/ftrace.c
index be1ca98fce5c..78174bbb257e 100644
--- a/arch/powerpc/kernel/trace/ftrace.c
+++ b/arch/powerpc/kernel/trace/ftrace.c
@@ -956,7 +956,7 @@ unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip)
return_hooker = ppc_function_entry(return_to_handler);
- if (!function_graph_enter(parent, ip, 0, NULL))
+ if (!function_graph_enter(parent, ip, 0, NULL, NULL))
parent = return_hooker;
out:
return parent;
diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
index b94d8db5ddcc..18f836727950 100644
--- a/arch/riscv/kernel/ftrace.c
+++ b/arch/riscv/kernel/ftrace.c
@@ -142,7 +142,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
*/
old = *parent;
- if (function_graph_enter(old, self_addr, frame_pointer, parent))
+ if (function_graph_enter(old, self_addr, frame_pointer, parent, NULL))
*parent = return_hooker;
}
diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c
index 1bb85f60c0dd..5021a23c5089 100644
--- a/arch/s390/kernel/ftrace.c
+++ b/arch/s390/kernel/ftrace.c
@@ -209,7 +209,7 @@ unsigned long prepare_ftrace_return(unsigned long ra, unsigned long sp,
if (unlikely(atomic_read(¤t->tracing_graph_pause)))
goto out;
ip -= MCOUNT_INSN_SIZE;
- if (!function_graph_enter(ra, ip, 0, (void *) sp))
+ if (!function_graph_enter(ra, ip, 0, (void *) sp), NULL)
ra = (unsigned long) return_to_handler;
out:
return ra;
diff --git a/arch/sh/kernel/ftrace.c b/arch/sh/kernel/ftrace.c
index 1b04270e5460..3a8271993e9c 100644
--- a/arch/sh/kernel/ftrace.c
+++ b/arch/sh/kernel/ftrace.c
@@ -364,7 +364,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
return;
}
- if (function_graph_enter(old, self_addr, 0, NULL))
+ if (function_graph_enter(old, self_addr, 0, NULL, NULL))
__raw_writel(old, parent);
}
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
diff --git a/arch/sparc/kernel/ftrace.c b/arch/sparc/kernel/ftrace.c
index 684b84ce397f..2783185719ba 100644
--- a/arch/sparc/kernel/ftrace.c
+++ b/arch/sparc/kernel/ftrace.c
@@ -130,7 +130,7 @@ unsigned long prepare_ftrace_return(unsigned long parent,
if (unlikely(atomic_read(¤t->tracing_graph_pause)))
return parent + 8UL;
- if (function_graph_enter(parent, self_addr, frame_pointer, NULL))
+ if (function_graph_enter(parent, self_addr, frame_pointer, NULL, NULL))
return parent + 8UL;
return return_hooker;
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index 024c3053dbba..a044734167af 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -1072,7 +1072,7 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
return;
}
- if (function_graph_enter(old, self_addr, frame_pointer, parent))
+ if (function_graph_enter(old, self_addr, frame_pointer, parent, NULL))
*parent = old;
}
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index f5aab37a8c34..e615b5e639aa 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -757,9 +757,12 @@ struct ftrace_graph_ret {
/* Type of the callback handlers for tracing function graph*/
typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *); /* return */
-typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); /* entry */
+/* @pt_regs is only available for CONFIG_FTRACE_FUNC_PROTOTYPE. */
+typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
+ struct pt_regs *); /* entry */
-extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace);
+int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
+ struct pt_regs *pt_regs);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
@@ -797,7 +800,8 @@ extern void return_to_handler(void);
extern int
function_graph_enter(unsigned long ret, unsigned long func,
- unsigned long frame_pointer, unsigned long *retp);
+ unsigned long frame_pointer, unsigned long *retp,
+ struct pt_regs *pt_regs);
struct ftrace_ret_stack *
ftrace_graph_get_ret_stack(struct task_struct *task, int idx);
diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
index 8dfd5021b933..7451dba84fee 100644
--- a/kernel/trace/fgraph.c
+++ b/kernel/trace/fgraph.c
@@ -96,8 +96,13 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,
return 0;
}
+/*
+ * Called from arch specific code. @pt_regs is only available for
+ * CONFIG_FTRACE_FUNC_PROTOTYPE.
+ */
int function_graph_enter(unsigned long ret, unsigned long func,
- unsigned long frame_pointer, unsigned long *retp)
+ unsigned long frame_pointer, unsigned long *retp,
+ struct pt_regs *pt_regs)
{
struct ftrace_graph_ent trace;
@@ -108,7 +113,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
goto out;
/* Only trace if the calling function expects to */
- if (!ftrace_graph_entry(&trace))
+ if (!ftrace_graph_entry(&trace, pt_regs))
goto out_ret;
return 0;
@@ -204,9 +209,11 @@ static struct notifier_block ftrace_suspend_notifier = {
/*
* Send the trace to the ring-buffer.
+ * @retval is only available for CONFIG_FTRACE_FUNC_PROTOTYPE.
* @return the original return address.
*/
-unsigned long ftrace_return_to_handler(unsigned long frame_pointer)
+unsigned long ftrace_return_to_handler(unsigned long frame_pointer,
+ unsigned long retval)
{
struct ftrace_graph_ret trace;
unsigned long ret;
@@ -327,7 +334,8 @@ void ftrace_graph_sleep_time_control(bool enable)
fgraph_sleep_time = enable;
}
-int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace)
+int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
+ struct pt_regs *pt_regs)
{
return 0;
}
@@ -417,11 +425,12 @@ ftrace_graph_probe_sched_switch(void *ignore, bool preempt,
next->ret_stack[index].calltime += timestamp;
}
-static int ftrace_graph_entry_test(struct ftrace_graph_ent *trace)
+static int ftrace_graph_entry_test(struct ftrace_graph_ent *trace,
+ struct pt_regs *pt_regs)
{
if (!ftrace_ops_test(&global_ops, trace->func, NULL))
return 0;
- return __ftrace_graph_entry(trace);
+ return __ftrace_graph_entry(trace, pt_regs);
}
/*
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 438b8b47198f..a1683cc55838 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -794,7 +794,9 @@ void ftrace_graph_graph_time_control(bool enable)
fgraph_graph_time = enable;
}
-static int profile_graph_entry(struct ftrace_graph_ent *trace)
+/* @pt_regs is only available for CONFIG_FTRACE_FUNC_PROTOTYPE. */
+static int profile_graph_entry(struct ftrace_graph_ent *trace,
+ struct pt_regs *pt_regs)
{
struct ftrace_ret_stack *ret_stack;
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 22433a15e340..4b31176d443e 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -737,7 +737,7 @@ void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
int trace_empty(struct trace_iterator *iter);
void trace_graph_return(struct ftrace_graph_ret *trace);
-int trace_graph_entry(struct ftrace_graph_ent *trace);
+int trace_graph_entry(struct ftrace_graph_ent *trace, struct pt_regs *pt_regs);
void set_graph_array(struct trace_array *tr);
void tracing_start_cmdline_record(void);
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index 78af97163147..f331a9ba946d 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -124,7 +124,7 @@ static inline int ftrace_graph_ignore_irqs(void)
return in_irq();
}
-int trace_graph_entry(struct ftrace_graph_ent *trace)
+int trace_graph_entry(struct ftrace_graph_ent *trace, struct pt_regs *pt_regs)
{
struct trace_array *tr = graph_array;
struct trace_array_cpu *data;
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index a745b0cee5d3..513e3544a45a 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -172,7 +172,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set)
return start_irqsoff_tracer(irqsoff_trace, set);
}
-static int irqsoff_graph_entry(struct ftrace_graph_ent *trace)
+static int irqsoff_graph_entry(struct ftrace_graph_ent *trace,
+ struct pt_regs *pt_regs)
{
struct trace_array *tr = irqsoff_trace;
struct trace_array_cpu *data;
diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
index 743b2b520d34..ce18f679930c 100644
--- a/kernel/trace/trace_sched_wakeup.c
+++ b/kernel/trace/trace_sched_wakeup.c
@@ -112,7 +112,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set)
return start_func_tracer(tr, set);
}
-static int wakeup_graph_entry(struct ftrace_graph_ent *trace)
+static int wakeup_graph_entry(struct ftrace_graph_ent *trace,
+ struct pt_regs *pt_regs)
{
struct trace_array *tr = wakeup_trace;
struct trace_array_cpu *data;
--
2.20.1
^ permalink raw reply related
* [PATCH 08/11] ftrace: introduce core part of function prototype recording
From: Changbin Du @ 2019-08-25 13:23 UTC (permalink / raw)
To: Steven Rostedt, Ingo Molnar
Cc: Jonathan Corbet, Jessica Yu, Thomas Gleixner, x86, linux-doc,
linux-kernel, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
linux-arch, linux-kbuild, Changbin Du
In-Reply-To: <20190825132330.5015-1-changbin.du@gmail.com>
This patch introduces the core part of our new CONFIG_FTRACE_FUNC_PROTOTYPE
feature. For arch which supports this feature must implement a new
arch-specific interface arch_fgraph_record_params().
In this patch, we add a new trace option "record-funcproto", and by now
only function graph tracer is supported. The major work is to handle
the printing stuff.
Here is an example of the graph trace of function pick_next_task_fair().
Note that we only record the parameter and return value of global
functions.
2) | pick_next_task_fair() {
2) | update_blocked_averages() {
2) 0.765 us | _raw_spin_lock_irqsave(lock=0xffff88807da2b100); /* ret=0x0000000000000082 */
2) 0.944 us | update_rq_clock(rq=0xffff88807da2b100);
2) 0.612 us | __update_load_avg_cfs_rq(now=0x000000251b8516ee, cfs_rq=0xffff8880754f7488); /* ret=0 */
2) 0.654 us | __update_load_avg_se(now=0x000000251b8516ee, cfs_rq=0xffff88807da2b180, se=0xffff88807be2e0d8); /* ret=0 */
2) 0.206 us | __update_load_avg_cfs_rq(now=0x000000251b8516ee, cfs_rq=0xffff88807da2b180); /* ret=0 */
2) | __update_load_avg_cfs_rq(now=0x000000251b8516ee, cfs_rq=0xffff888079b5fb18) {
2) 2.410 us | __accumulate_pelt_segments();
2) 3.103 us | } /* ret=1 */
2) 0.193 us | __update_load_avg_cfs_rq(now=0x000000251b8516ee, cfs_rq=0xffff88807da2b180); /* ret=0 */
2) | update_rt_rq_load_avg(now=0x000000251b8516ee, rq=0xffff88807da2b100, running=0) {
2) 0.258 us | __accumulate_pelt_segments();
2) 1.617 us | } /* ret=1 */
2) | update_dl_rq_load_avg(now=0x000000251b8516ee, rq=0xffff88807da2b100, running=0) {
2) 0.230 us | __accumulate_pelt_segments();
2) 1.511 us | } /* ret=1 */
2) 1.040 us | _raw_spin_unlock_irqrestore(lock=0xffff88807da2b100, flags=0x0000000000000082);
2) + 14.739 us | }
2) | load_balance() {
2) | find_busiest_group() {
2) 0.874 us | update_group_capacity(sd=0xffff88807c1d37d0, cpu=2);
2) 1.761 us | idle_cpu();
2) 0.262 us | idle_cpu();
2) 0.217 us | idle_cpu();
2) 6.338 us | }
2) 8.442 us | }
2) 1.823 us | __msecs_to_jiffies(m=0x00000006); /* ret=0x0000000000000002 */
2) | load_balance() {
2) | find_busiest_group() {
2) 0.434 us | idle_cpu();
2) 0.233 us | idle_cpu();
2) 0.210 us | idle_cpu();
2) 2.308 us | }
2) 2.821 us | }
2) 0.263 us | __msecs_to_jiffies(m=0x00000008); /* ret=0x0000000000000002 */
2) 0.977 us | _raw_spin_lock(lock=0xffff88807da2b100);
2) + 32.262 us | }
The printing rules of each value is:
o For signed value, it is always printed as decimal number.
o For unsigned value,
- For value has size great than 8, it is printed as '{..}'.
- For value has size of 1,2,4,8, it is printed as hexadecimal number.
- If failed to record a parameter, it is printed as '?'.
Signed-off-by: Changbin Du <changbin.du@gmail.com>
---
include/linux/ftrace.h | 27 +++++++
kernel/trace/fgraph.c | 5 ++
kernel/trace/ftrace.c | 50 +++++++++++++
kernel/trace/trace.h | 8 ++
kernel/trace/trace_entries.h | 10 +++
kernel/trace/trace_functions_graph.c | 106 +++++++++++++++++++++++++--
6 files changed, 201 insertions(+), 5 deletions(-)
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index e615b5e639aa..82b92d355431 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/fs.h>
+#include <linux/trace_seq.h>
#include <asm/ftrace.h>
@@ -377,6 +378,9 @@ struct func_prototype {
#define FTRACE_PROTOTYPE_SIGNED(t) (t & BIT(7))
#define FTRACE_PROTOTYPE_SIZE(t) (t & GENMASK(6, 0))
+
+void ftrace_print_typed_val(struct trace_seq *s, uint8_t type,
+ unsigned long val);
#endif
int ftrace_force_update(void);
@@ -731,6 +735,13 @@ extern void ftrace_init(void);
static inline void ftrace_init(void) { }
#endif
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+# define FTRACE_MAX_FUNC_PARAMS 10
+
+# define FTRACE_PROTOTYPE_SIGNED(t) (t & BIT(7))
+# define FTRACE_PROTOTYPE_SIZE(t) (t & GENMASK(6, 0))
+#endif
+
/*
* Structure that defines an entry function trace.
* It's already packed but the attribute "packed" is needed
@@ -739,6 +750,12 @@ static inline void ftrace_init(void) { }
struct ftrace_graph_ent {
unsigned long func; /* Current function */
int depth;
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ uint8_t nr_param;
+ char *param_names[FTRACE_MAX_FUNC_PARAMS];
+ uint8_t param_types[FTRACE_MAX_FUNC_PARAMS];
+ unsigned long param_values[FTRACE_MAX_FUNC_PARAMS];
+#endif
} __packed;
/*
@@ -753,8 +770,13 @@ struct ftrace_graph_ret {
unsigned long long calltime;
unsigned long long rettime;
int depth;
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ uint8_t ret_type;
+ unsigned long retval;
+#endif
} __packed;
+
/* Type of the callback handlers for tracing function graph*/
typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *); /* return */
/* @pt_regs is only available for CONFIG_FTRACE_FUNC_PROTOTYPE. */
@@ -842,6 +864,11 @@ static inline void unpause_graph_tracing(void)
{
atomic_dec(¤t->tracing_graph_pause);
}
+
+void arch_fgraph_record_params(struct ftrace_graph_ent *trace,
+ struct func_prototype *proto,
+ struct pt_regs *pt_regs);
+
#else /* !CONFIG_FUNCTION_GRAPH_TRACER */
#define __notrace_funcgraph
diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
index 7451dba84fee..26e452418249 100644
--- a/kernel/trace/fgraph.c
+++ b/kernel/trace/fgraph.c
@@ -220,6 +220,11 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer,
ftrace_pop_return_trace(&trace, &ret, frame_pointer);
trace.rettime = trace_clock_local();
+
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ trace.retval = retval;
+#endif
+
ftrace_graph_return(&trace);
/*
* The ftrace_graph_return() may still access the current
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index a1683cc55838..1e6a96f1986b 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5658,6 +5658,56 @@ static int ftrace_process_funcproto(struct module *mod,
return ret;
}
+
+void ftrace_print_typed_val(struct trace_seq *s, uint8_t type,
+ unsigned long val)
+{
+ unsigned int sz = FTRACE_PROTOTYPE_SIZE(type);
+ bool is_signed = FTRACE_PROTOTYPE_SIGNED(type);
+
+ /* Don't show complex types */
+ if (sz > sizeof(long)) {
+ trace_seq_printf(s, "{..}");
+ return;
+ }
+
+ switch (sz) {
+ case 0:
+ /* The value is not valid. */
+ trace_seq_printf(s, "?");
+ break;
+ case 1:
+ val &= GENMASK_ULL(7, 0);
+ if (is_signed)
+ trace_seq_printf(s, "%d", (char)val);
+ else
+ trace_seq_printf(s, "0x%02lx", val);
+ break;
+ case 2:
+ val &= GENMASK_ULL(15, 0);
+ if (is_signed)
+ trace_seq_printf(s, "%d", (short)val);
+ else
+ trace_seq_printf(s, "0x%04lx", val);
+ break;
+ case 4:
+ val &= GENMASK_ULL(31, 0);
+ if (is_signed)
+ trace_seq_printf(s, "%d", (int)val);
+ else
+ trace_seq_printf(s, "0x%08lx", val);
+ break;
+ case 8:
+ val &= GENMASK_ULL(63, 0);
+ if (is_signed)
+ trace_seq_printf(s, "%lld", (long long)val);
+ else
+ trace_seq_printf(s, "0x%016lx", val);
+ break;
+ default:
+ trace_seq_printf(s, "{badsize%d}", sz);
+ }
+}
#endif
struct ftrace_mod_func {
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 4b31176d443e..f10acad0140f 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1231,6 +1231,13 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
# define STACK_FLAGS
#endif
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+# define FUNCPROTO_FLAGS \
+ C(RECORD_FUNCPROTO, "record-funcproto"),
+#else
+# define FUNCPROTO_FLAGS
+#endif
+
/*
* trace_iterator_flags is an enumeration that defines bit
* positions into trace_flags that controls the output.
@@ -1256,6 +1263,7 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
C(LATENCY_FMT, "latency-format"), \
C(RECORD_CMD, "record-cmd"), \
C(RECORD_TGID, "record-tgid"), \
+ FUNCPROTO_FLAGS \
C(OVERWRITE, "overwrite"), \
C(STOP_ON_FREE, "disable_on_free"), \
C(IRQ_INFO, "irq-info"), \
diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h
index fc8e97328e54..68b044ea8440 100644
--- a/kernel/trace/trace_entries.h
+++ b/kernel/trace/trace_entries.h
@@ -82,6 +82,12 @@ FTRACE_ENTRY_PACKED(funcgraph_entry, ftrace_graph_ent_entry,
__field_struct( struct ftrace_graph_ent, graph_ent )
__field_desc( unsigned long, graph_ent, func )
__field_desc( int, graph_ent, depth )
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ __field_desc( unsigned char, graph_ent, nr_param )
+ __array_desc( char *, graph_ent, param_names, FTRACE_MAX_FUNC_PARAMS)
+ __array_desc( uint8_t, graph_ent, param_types, FTRACE_MAX_FUNC_PARAMS)
+ __array_desc( unsigned long, graph_ent, param_values, FTRACE_MAX_FUNC_PARAMS)
+#endif
),
F_printk("--> %ps (%d)", (void *)__entry->func, __entry->depth),
@@ -101,6 +107,10 @@ FTRACE_ENTRY_PACKED(funcgraph_exit, ftrace_graph_ret_entry,
__field_desc( unsigned long long, ret, rettime )
__field_desc( unsigned long, ret, overrun )
__field_desc( int, ret, depth )
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ __field_desc( unsigned char, ret, ret_type)
+ __field_desc( unsigned long, ret, retval )
+#endif
),
F_printk("<-- %ps (%d) (start: %llx end: %llx) over: %d",
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index f331a9ba946d..ba4eb71646e9 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -169,6 +169,17 @@ int trace_graph_entry(struct ftrace_graph_ent *trace, struct pt_regs *pt_regs)
if (tracing_thresh)
return 1;
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ trace->nr_param = 0;
+ if (tr->trace_flags & TRACE_ITER_RECORD_FUNCPROTO) {
+ struct ftrace_func_entry *ent;
+
+ ent = ftrace_lookup_ip(ftrace_prototype_hash, trace->func);
+ if (ent)
+ arch_fgraph_record_params(trace, ent->priv, pt_regs);
+ }
+#endif
+
local_irq_save(flags);
cpu = raw_smp_processor_id();
data = per_cpu_ptr(tr->trace_buffer.data, cpu);
@@ -250,6 +261,21 @@ void trace_graph_return(struct ftrace_graph_ret *trace)
return;
}
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+ if (tr->trace_flags & TRACE_ITER_RECORD_FUNCPROTO) {
+ struct ftrace_func_entry *ent;
+
+ ent = ftrace_lookup_ip(ftrace_prototype_hash, trace->func);
+ if (ent) {
+ /* The retval has been saved by trace_graph_return(). */
+ trace->ret_type =
+ ((struct func_prototype *)ent->priv)->ret_type;
+ } else
+ trace->ret_type = 0;
+ } else
+ trace->ret_type = 0;
+#endif
+
local_irq_save(flags);
cpu = raw_smp_processor_id();
data = per_cpu_ptr(tr->trace_buffer.data, cpu);
@@ -380,6 +406,71 @@ static void print_graph_lat_fmt(struct trace_seq *s, struct trace_entry *entry)
trace_seq_puts(s, " | ");
}
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+static void print_graph_params(struct trace_seq *s,
+ struct ftrace_graph_ent *call,
+ struct ftrace_graph_ret *graph_ret)
+{
+ int i;
+
+ BUG_ON(call->nr_param > FTRACE_MAX_FUNC_PARAMS);
+
+ trace_seq_printf(s, "%ps(", (void *)call->func);
+ for (i = 0; i < call->nr_param; i++) {
+ if (i > 0)
+ trace_seq_printf(s, ", ");
+ trace_seq_printf(s, "%s=", call->param_names[i]);
+ ftrace_print_typed_val(s, call->param_types[i],
+ call->param_values[i]);
+ }
+
+ if (graph_ret) {
+ /* leaf */
+ if (graph_ret->ret_type) {
+ trace_seq_printf(s, "); /* ret=");
+ ftrace_print_typed_val(s, graph_ret->ret_type,
+ graph_ret->retval);
+ trace_seq_puts(s, " */\n");
+ } else
+ trace_seq_puts(s, ");\n");
+ } else
+ trace_seq_printf(s, ") {\n");
+}
+
+static void print_graph_retval(struct trace_seq *s,
+ struct ftrace_graph_ret *trace,
+ bool tail)
+{
+ if (trace->ret_type) {
+ if (tail)
+ trace_seq_puts(s, ", ");
+ else
+ trace_seq_puts(s, " /* ");
+
+ trace_seq_printf(s, "ret=");
+ ftrace_print_typed_val(s, trace->ret_type, trace->retval);
+
+ trace_seq_printf(s, " */");
+ }
+}
+#else
+static void print_graph_params(struct trace_seq *s,
+ struct ftrace_graph_ent *call,
+ struct ftrace_graph_ret *graph_ret)
+{
+ if (graph_ret)
+ trace_seq_printf(s, "%ps();\n", (void *)call->func);
+ else
+ trace_seq_printf(s, "%ps() {\n", (void *)call->func);
+}
+
+static void print_graph_retval(struct trace_seq *s,
+ struct ftrace_graph_ret *trace,
+ bool tail)
+{
+}
+#endif
+
/* If the pid changed since the last trace, output this event */
static void
verif_pid(struct trace_seq *s, pid_t pid, int cpu, struct fgraph_data *data)
@@ -665,7 +756,7 @@ print_graph_entry_leaf(struct trace_iterator *iter,
for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++)
trace_seq_putc(s, ' ');
- trace_seq_printf(s, "%ps();\n", (void *)call->func);
+ print_graph_params(s, call, graph_ret);
print_graph_irq(iter, graph_ret->func, TRACE_GRAPH_RET,
cpu, iter->ent->pid, flags);
@@ -703,7 +794,7 @@ print_graph_entry_nested(struct trace_iterator *iter,
for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++)
trace_seq_putc(s, ' ');
- trace_seq_printf(s, "%ps() {\n", (void *)call->func);
+ print_graph_params(s, call, NULL);
if (trace_seq_has_overflowed(s))
return TRACE_TYPE_PARTIAL_LINE;
@@ -950,10 +1041,15 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
* belongs to, write out the function name. Always do
* that if the funcgraph-tail option is enabled.
*/
- if (func_match && !(flags & TRACE_GRAPH_PRINT_TAIL))
- trace_seq_puts(s, "}\n");
- else
+ if (func_match && !(flags & TRACE_GRAPH_PRINT_TAIL)) {
+ trace_seq_puts(s, "}");
+ print_graph_retval(s, trace, false);
+ trace_seq_puts(s, "\n");
+ } else {
trace_seq_printf(s, "} /* %ps */\n", (void *)trace->func);
+ print_graph_retval(s, trace, true);
+ trace_seq_puts(s, "\n");
+ }
/* Overrun */
if (flags & TRACE_GRAPH_PRINT_OVERRUN)
--
2.20.1
^ permalink raw reply related
* [PATCH 09/11] x86_64: add function prototype recording support
From: Changbin Du @ 2019-08-25 13:23 UTC (permalink / raw)
To: Steven Rostedt, Ingo Molnar
Cc: Jonathan Corbet, Jessica Yu, Thomas Gleixner, x86, linux-doc,
linux-kernel, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
linux-arch, linux-kbuild, Changbin Du
In-Reply-To: <20190825132330.5015-1-changbin.du@gmail.com>
This patch implements the arch_fgraph_record_params() function for x86_64
platform and deliver the return value of function to ftrace core part.
Signed-off-by: Changbin Du <changbin.du@gmail.com>
---
arch/x86/Kconfig | 1 +
arch/x86/kernel/ftrace.c | 84 +++++++++++++++++++++++++++++++++++--
arch/x86/kernel/ftrace_64.S | 4 +-
3 files changed, 85 insertions(+), 4 deletions(-)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 222855cc0158..34e583bfdab8 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -31,6 +31,7 @@ config X86_64
select NEED_DMA_MAP_STATE
select SWIOTLB
select ARCH_HAS_SYSCALL_WRAPPER
+ select HAVE_FTRACE_FUNC_PROTOTYPE
config FORCE_DYNAMIC_FTRACE
def_bool y
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index a044734167af..fc0a062ce762 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -31,6 +31,7 @@
#include <asm/ftrace.h>
#include <asm/nops.h>
#include <asm/text-patching.h>
+#include <asm-generic/dwarf.h>
#ifdef CONFIG_DYNAMIC_FTRACE
@@ -918,7 +919,8 @@ static void *addr_from_call(void *ptr)
}
void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
- unsigned long frame_pointer);
+ unsigned long frame_pointer,
+ struct pt_regs *pt_regs);
/*
* If the ops->trampoline was not allocated, then it probably
@@ -973,6 +975,82 @@ void arch_ftrace_trampoline_free(struct ftrace_ops *ops)
ops->trampoline = 0;
}
+#ifdef CONFIG_FTRACE_FUNC_PROTOTYPE
+void arch_fgraph_record_params(struct ftrace_graph_ent *trace,
+ struct func_prototype *proto,
+ struct pt_regs *pt_regs)
+{
+ int i;
+
+ trace->nr_param = min(proto->nr_param, (uint8_t)FTRACE_MAX_FUNC_PARAMS);
+
+ for (i = 0; i < trace->nr_param; i++) {
+ struct func_param *param = &proto->params[i];
+ unsigned int sz = FTRACE_PROTOTYPE_SIZE(param->type);
+ long off = (char)param->loc[1];
+ unsigned long value = 0;
+ bool good = true;
+
+ if (sz > sizeof(value)) {
+ /* Don't record value of complex type. */
+ trace->param_types[i] = param->type;
+ trace->param_values[i] = 0;
+ continue;
+ }
+
+ switch (param->loc[0]) {
+ case DW_OP_reg1:
+ value = pt_regs->dx;
+ break;
+ case DW_OP_reg2:
+ value = pt_regs->cx;
+ break;
+ case DW_OP_reg3:
+ value = pt_regs->bx;
+ break;
+ case DW_OP_reg4:
+ value = pt_regs->si;
+ break;
+ case DW_OP_reg5:
+ value = pt_regs->di;
+ break;
+ case DW_OP_reg6:
+ value = pt_regs->bp;
+ break;
+ case DW_OP_reg8:
+ value = pt_regs->r8;
+ break;
+ case DW_OP_reg9:
+ value = pt_regs->r9;
+ break;
+ case DW_OP_fbreg:
+ if (probe_kernel_read(&value,
+ (void *)pt_regs->bp + off,
+ sz))
+ good = false;
+ break;
+ case DW_OP_breg7:
+ if (probe_kernel_read(&value,
+ (void *)pt_regs->sp + off,
+ sz))
+ good = false;
+ break;
+ default:
+ /* unexpected loc expression */
+ good = false;
+ }
+
+ trace->param_names[i] = param->name;
+ if (good) {
+ trace->param_types[i] = param->type;
+ trace->param_values[i] = value;
+ } else {
+ /* set the type to 0 so we skip it when printing. */
+ trace->param_types[i] = 0;
+ }
+ }
+}
+#endif /* CONFIG_FTRACE_FUNC_PROTOTYPE */
#endif /* CONFIG_X86_64 */
#endif /* CONFIG_DYNAMIC_FTRACE */
@@ -1017,7 +1095,7 @@ int ftrace_disable_ftrace_graph_caller(void)
* in current thread info.
*/
void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
- unsigned long frame_pointer)
+ unsigned long frame_pointer, struct pt_regs *pt_regs)
{
unsigned long old;
int faulted;
@@ -1072,7 +1150,7 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
return;
}
- if (function_graph_enter(old, self_addr, frame_pointer, parent, NULL))
+ if (function_graph_enter(old, self_addr, frame_pointer, parent, pt_regs))
*parent = old;
}
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S
index 809d54397dba..e01d6358e859 100644
--- a/arch/x86/kernel/ftrace_64.S
+++ b/arch/x86/kernel/ftrace_64.S
@@ -289,7 +289,8 @@ ENTRY(ftrace_graph_caller)
leaq MCOUNT_REG_SIZE+8(%rsp), %rsi
movq $0, %rdx /* No framepointers needed */
- call prepare_ftrace_return
+ movq %rsp, %rcx /* the fourth parameter */
+ call prepare_ftrace_return
restore_mcount_regs
@@ -304,6 +305,7 @@ ENTRY(return_to_handler)
movq %rax, (%rsp)
movq %rdx, 8(%rsp)
movq %rbp, %rdi
+ movq %rax, %rsi
call ftrace_return_to_handler
--
2.20.1
^ permalink raw reply related
* [PATCH 10/11] ftrace: add doc for new option record-funcproto
From: Changbin Du @ 2019-08-25 13:23 UTC (permalink / raw)
To: Steven Rostedt, Ingo Molnar
Cc: Jonathan Corbet, Jessica Yu, Thomas Gleixner, x86, linux-doc,
linux-kernel, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
linux-arch, linux-kbuild, Changbin Du
In-Reply-To: <20190825132330.5015-1-changbin.du@gmail.com>
Just add the doc for our new feature.
Signed-off-by: Changbin Du <changbin.du@gmail.com>
---
Documentation/trace/ftrace.rst | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/trace/ftrace.rst b/Documentation/trace/ftrace.rst
index f60079259669..c68fbbedb8bd 100644
--- a/Documentation/trace/ftrace.rst
+++ b/Documentation/trace/ftrace.rst
@@ -988,6 +988,7 @@ To see what is available, simply cat the file::
nolatency-format
record-cmd
norecord-tgid
+ norecord-funcproto
overwrite
nodisable_on_free
irq-info
@@ -1131,6 +1132,11 @@ Here are the available options:
mapped Thread Group IDs (TGID) mapping to pids. See
"saved_tgids".
+ record-funcproto
+ Record function parameters and return value. This option
+ is only supported by function_graph tracer on x86_64
+ platform by now.
+
overwrite
This controls what happens when the trace buffer is
full. If "1" (default), the oldest events are
--
2.20.1
^ permalink raw reply related
* [PATCH 11/11] MAINTAINERS: make scripts/ftrace/ maintained
From: Changbin Du @ 2019-08-25 13:23 UTC (permalink / raw)
To: Steven Rostedt, Ingo Molnar
Cc: Jonathan Corbet, Jessica Yu, Thomas Gleixner, x86, linux-doc,
linux-kernel, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
linux-arch, linux-kbuild, Changbin Du
In-Reply-To: <20190825132330.5015-1-changbin.du@gmail.com>
Make scripts/ftrace/ maintained and I would like to help with reviewing
related patches.
Signed-off-by: Changbin Du <changbin.du@gmail.com>
---
MAINTAINERS | 2 ++
1 file changed, 2 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 9cbcf167bdd0..ca012ea260d7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16293,6 +16293,7 @@ F: drivers/char/tpm/
TRACING
M: Steven Rostedt <rostedt@goodmis.org>
M: Ingo Molnar <mingo@redhat.com>
+R: Changbin Du <changbin.du@gmail.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git perf/core
S: Maintained
F: Documentation/trace/ftrace.rst
@@ -16303,6 +16304,7 @@ F: include/linux/trace*.h
F: include/trace/
F: kernel/trace/
F: tools/testing/selftests/ftrace/
+F: scripts/ftrace/
TRACING MMIO ACCESSES (MMIOTRACE)
M: Steven Rostedt <rostedt@goodmis.org>
--
2.20.1
^ permalink raw reply related
* Re: [PATCH v2] Ext4 documentation fixes.
From: Jonathan Corbet @ 2019-08-25 14:42 UTC (permalink / raw)
To: Theodore Y. Ts'o
Cc: Andreas Dilger, Ayush Ranjan, Andreas Dilger,
Ext4 Developers List, linux-doc, Linux Kernel Mailing List
In-Reply-To: <20190824230930.GB5163@mit.edu>
On Sat, 24 Aug 2019 19:09:30 -0400
"Theodore Y. Ts'o" <tytso@mit.edu> wrote:
> If you haven't pushed out your doc tree, can you please drop it? I've
> already applied an (improved) version of this patch to the ext4 tree,
> and I actually have some plans to do further fixups of ext4 on-disk
> format documentation in the ext4 tree. So it would be easier if we
> keep ext4 documentation updates in the ext4 git tree.
Sigh...I checked linux-next first and saw no signs of activity there. Oh
well; I guess I can disappear that change.
jon
^ permalink raw reply
* [RFC PATCH v3 00/19] tracing: skc: Boot-time tracing and Supplemental Kernel Cmdline
From: Masami Hiramatsu @ 2019-08-26 3:15 UTC (permalink / raw)
To: Steven Rostedt, Frank Rowand
Cc: Ingo Molnar, Namhyung Kim, Tim Bird, Jiri Olsa,
Arnaldo Carvalho de Melo, Tom Zanussi, Rob Herring, Andrew Morton,
Thomas Gleixner, Greg Kroah-Hartman, Alexey Dobriyan,
Jonathan Corbet, Linus Torvalds, linux-doc, linux-fsdevel,
linux-kernel
Hello,
This is the 3rd version of RFC series for the boot-time tracing.
Previous thread is here.
https://lkml.kernel.org/r/156316746861.23477.5815110570539190650.stgit@devnote2
On that thread and offline talk with Frank Rowand at OSS Japan2019
last month, we agreed that the devicetree is only for hardware
description and should not expand it for software settings.
Thus, in this version, I introduced a new extended command line
feature called Supplemental Kernel Cmdline (SKC) instead of
devicetree, and rewrote the boot-time tracing part based on that.
Supplemental Kernel Cmdline
===========================
Supplemental kernel command line (SKC) allows admin to pass a
tree-structured supplemental kernel commandline file (SKC file)
when boot up kernel. This expands the kernel command line in
efficient way.
Each key is described as a dot-jointed-words. And user can write
the key-words in tree stlye.
For example,
feature.option.foo = 1;
feature.option.bar = 2;
can be also written in
feature.option {
foo = 1;
bar = 2;
}
(Note that in both style, the same words are merged automatically
and make a single tree)
All values are treated as a string, or array of strings, e.g.
feature.options = "foo", "bar";
User can see the loaded SKC key-value list via /proc/skc.
The size of SKC is limited upto 32KB and 512 key-words and values
in total.
SKC and Bootloader
==================
To play with SKC, you need to use patched qemu or patched grub.
Please checkout below for x86 support. (for PoC, I implemented it
only for x86, but it is not so hard to do same on other archs)
https://github.com/mhiramat/qemu.git skc
https://github.com/mhiramat/grub.git grub-x86-skc
User can pass an skc file when boot the qemu machine with "-skc PATH"
option. Or specify the skc file on grub console by "skc PATH".
Boot-time Tracing
=================
Boot-time tracing side has been updated for SKC, but the difference
is small, because SKC has similar tree-based interfaces to OF APIs.
Currently, it supports following SKC options. Please read
Documentation/trace/boottime-trace.rst for details.
- ftrace.options = OPT1[,OPT2...];
- ftrace.trace_clock = CLOCK;
- ftrace.dump_on_oops [= MODE];
- ftrace.traceoff_on_warning;
- ftrace.tp_printk;
- ftrace.buffer_size = SIZE;
- ftrace.alloc_snapshot;
- ftrace.events = EVENT[, EVENT2...];
- ftrace.tracer = TRACER;
- ftrace.event.GROUP.EVENT.filter = FILTER;
- ftrace.event.GROUP.EVENT.actions = ACTION[, ACTION2...];
- ftrace.event.GROUP.EVENT.enable;
- ftrace.event.kprobes.EVENT.probes = PROBE[, PROBE2...];
- ftrace.event.synthetic.EVENT.fields = FIELD[, FIELD2...];
- ftrace.[instance.INSTANCE.]cpumask = CPUMASK;
- ftrace.[instance.INSTANCE.]ftrace.filters
- ftrace.[instance.INSTANCE.]ftrace.notraces
- ftrace.fgraph.filters = FILTER[, FILTER2...];
- ftrace.fgraph.notraces = FILTER[, FILTER2...];
- ftrace.fgraph.max_depth = MAX_DEPTH;
This series can be applied on Steve's tracing tree (ftrace/core) or
available on below
https://github.com/mhiramat/linux.git ftrace-boottrace-v3
Usage
=====
With this series, we can setup new kprobe and synthetic events, more
complicated event filters and trigger actions including histogram
via supplemental kernel cmdline.
We can add filter and actions for each event, define kprobe events,
and synthetic events with histogram like below.
ftrace.event {
task.task_newtask {
filter = "pid < 128";
enable;
}
kprobes.vfs_read {
probes = "vfs_read $arg1 $arg2";
filter = "common_pid < 200";
enable;
}
synthetic.initcall_latency {
fields = "unsigned long func", "u64 lat";
actions = "hist:keys=func.sym,lat:vals=lat:sort=lat";
}
initcall.initcall_start {
actions = "hist:keys=func:ts0=common_timestamp.usecs";
}
initcall.initcall_finish {
actions = "hist:keys=func:lat=common_timestamp.usecs-$ts0:onmatch(initcall.initcall_start).initcall_latency(func,$lat)";
}
}
Also, this supports "instance" node, which allows us to run several
tracers for different purpose at once. For example, one tracer is for
tracing functions in module alpha, and others tracing module beta,
you can write followings.
ftrace.instance {
foo {
tracer = "function";
ftrace-filters = "*:mod:alpha";
}
bar {
tracer = "function";
ftrace-filters = "*:mod:beta";
}
}
The instance node also accepts event nodes so that each instance
can customize its event tracing.
This boot-time trace also supports ftrace kernel parameters.
For example, following kernel parameters
trace_options=sym-addr trace_event=initcall:* tp_printk trace_buf_size=1M ftrace=function ftrace_filter="vfs*"
it can be written in SKC like below.
ftrace {
options = sym-addr;
events = "initcall:*";
tp-printk;
buffer-size = 1MB;
ftrace-filters = "vfs*";
}
However, since the initialization timing is different, if you need
to trace very early boot, please use normal kernel parameters.
Some Notes
==========
- For specifying the skc data address, there are 2 ways to pass it
to kernel. One is expanding arch-specific entries (e.g. setup_data
on x86), another is passing it via kernel cmdline. The former may
need to expand devicetree anyway (we need /chosen/skc_addr property
for some arch.) The latter is more generic, but it may not work if
the space on cmdline shorts.
According to Frank's suggestion, I introduced "skc=PADDR,SIZE" option
to kernel cmdline, boot loaders must support it. This is generic, and
I still have some concerns, like memory reservation. In this version,
I used early_param() to reserve SKC memory. If it is too late, we'd
better considering to use arch specific data passing.
- Currently, supplemental kernel cmdline doesn't support __setup()
routine to avoid confusion, but I think it is possible to support it.
- For saving memory consuming after boot, all variables and APIs are
__init and __initdata. Thus runtime memory footprint becomes minimum.
But if some loadable modules wants to use it. We have to keep at least
query interface and tree nodes on memory.
- As you can see, the EVENT.actions value is a bit ugly. Maybe we can
introduce sub-nodes under action node, something like below.
ftrace.event.initcall.initcall_finish.action.hist {
keys = "func";
lat = "common_timestamp.usecs-$ts0";
onmatch = "initcall.initcall_start";
call = "initcall_latency(func,$lat)";
}
Any suggestions, thoughts?
Thank you,
---
Masami Hiramatsu (19):
skc: Add supplemental kernel cmdline support
skc: Add /proc/sup_cmdline to show SKC key-value list
skc: Add a boot setup routine from cmdline
Documentation: skc: Add a doc for supplemental kernel cmdline
tracing: Apply soft-disabled and filter to tracepoints printk
tracing: kprobes: Output kprobe event to printk buffer
tracing: Expose EXPORT_SYMBOL_GPL symbol
tracing: kprobes: Register to dynevent earlier stage
tracing: Accept different type for synthetic event fields
tracing: Add NULL trace-array check in print_synth_event()
tracing/boot: Add boot-time tracing by supplemental kernel cmdline
tracing/boot: Add per-event settings
tracing/boot Add kprobe event support
tracing/boot: Add synthetic event support
tracing/boot: Add instance node support
tracing/boot: Add cpu_mask option support
tracing/boot: Add function tracer filter options
tracing/boot: Add function-graph tracer options
Documentation: tracing: Add boot-time tracing document
Documentation/admin-guide/index.rst | 1
Documentation/admin-guide/kernel-parameters.txt | 6
Documentation/admin-guide/skc.rst | 123 ++++
Documentation/trace/boottime-trace.rst | 185 ++++++
MAINTAINERS | 8
arch/Kconfig | 9
fs/proc/Makefile | 1
fs/proc/sup_cmdline.c | 106 ++++
include/linux/skc.h | 205 +++++++
include/linux/trace_events.h | 1
init/main.c | 54 ++
kernel/trace/Kconfig | 9
kernel/trace/Makefile | 1
kernel/trace/ftrace.c | 85 ++-
kernel/trace/trace.c | 90 ++-
kernel/trace/trace_boot.c | 457 +++++++++++++++
kernel/trace/trace_events.c | 3
kernel/trace/trace_events_hist.c | 14
kernel/trace/trace_events_trigger.c | 2
kernel/trace/trace_kprobe.c | 81 ++-
lib/Kconfig | 3
lib/Makefile | 2
lib/skc.c | 694 +++++++++++++++++++++++
23 files changed, 2044 insertions(+), 96 deletions(-)
create mode 100644 Documentation/admin-guide/skc.rst
create mode 100644 Documentation/trace/boottime-trace.rst
create mode 100644 fs/proc/sup_cmdline.c
create mode 100644 include/linux/skc.h
create mode 100644 kernel/trace/trace_boot.c
create mode 100644 lib/skc.c
--
Masami Hiramatsu (Linaro) <mhiramat@kernel.org>
^ permalink raw reply
* [RFC PATCH v3 01/19] skc: Add supplemental kernel cmdline support
From: Masami Hiramatsu @ 2019-08-26 3:15 UTC (permalink / raw)
To: Steven Rostedt, Frank Rowand
Cc: Ingo Molnar, Namhyung Kim, Tim Bird, Jiri Olsa,
Arnaldo Carvalho de Melo, Tom Zanussi, Rob Herring, Andrew Morton,
Thomas Gleixner, Greg Kroah-Hartman, Alexey Dobriyan,
Jonathan Corbet, Linus Torvalds, linux-doc, linux-fsdevel,
linux-kernel
In-Reply-To: <156678933823.21459.4100380582025186209.stgit@devnote2>
Supplemental kernel command line (SKC) allows admin to pass a
tree-structured supplemental kernel commandline file (SKC file)
when boot up kernel. This expands the kernel command line in
efficient way.
SKC file will contain some key-value commands, e.g.
key.word = value1;
another.key.word = value2;
It can fold same keys with braces, also you can write array
data. For example,
key {
word1 {
setting1 = data;
setting2;
}
word2.array = "val1", "val2";
}
User can access these key-value pair and tree structure via
SKC APIs.
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
---
MAINTAINERS | 6
arch/Kconfig | 9 +
include/linux/skc.h | 205 +++++++++++++++
lib/Kconfig | 3
lib/Makefile | 2
lib/skc.c | 694 +++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 919 insertions(+)
create mode 100644 include/linux/skc.h
create mode 100644 lib/skc.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 035ffc1e16a3..67590c0e37c5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15362,6 +15362,12 @@ W: http://www.stlinux.com
S: Supported
F: drivers/net/ethernet/stmicro/stmmac/
+SUPPLEMENTAL KERNEL CMDLINE
+M: Masami Hiramatsu <mhiramat@kernel.org>
+S: Maintained
+F: lib/skc.c
+F: include/linux/skc.h
+
SUN3/3X
M: Sam Creasey <sammy@sammy.net>
W: http://sammy.net/sun3/
diff --git a/arch/Kconfig b/arch/Kconfig
index a7b57dd42c26..14d709ef0396 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -24,6 +24,15 @@ config HAVE_IMA_KEXEC
config HOTPLUG_SMT
bool
+config SKC
+ bool "Supplemental kernel cmdline"
+ select LIBSKC
+ default y
+ help
+ Supplemental kernel command line allows system admin to pass a
+ text file as complemental extension of kernel cmdline when boot.
+ The bootloader needs to support loading the SKC file.
+
config OPROFILE
tristate "OProfile system profiling"
depends on PROFILING
diff --git a/include/linux/skc.h b/include/linux/skc.h
new file mode 100644
index 000000000000..71e485ce947f
--- /dev/null
+++ b/include/linux/skc.h
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_SKC_H
+#define _LINUX_SKC_H
+/*
+ * Supplemental Kernel Command Line
+ * Copyright (C) 2019 Linaro Ltd.
+ * Author: Masami Hiramatsu <mhiramat@kernel.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+/* SKC tree node */
+struct skc_node {
+ u16 next;
+ u16 child;
+ u16 parent;
+ u16 data;
+} __attribute__ ((__packed__));
+
+#define SKC_KEY 0
+#define SKC_VALUE (1 << 15)
+/* Maximum size of supplemental kernel cmdline is 32KB - 1 */
+#define SKC_DATA_MAX (SKC_VALUE - 1)
+
+#define SKC_NODE_MAX 512
+#define SKC_KEYLEN_MAX 256
+#define SKC_DEPTH_MAX 16
+
+/* Node tree access raw APIs */
+struct skc_node * __init skc_root_node(void);
+int __init skc_node_index(struct skc_node *node);
+struct skc_node * __init skc_node_get_parent(struct skc_node *node);
+struct skc_node * __init skc_node_get_child(struct skc_node *node);
+struct skc_node * __init skc_node_get_next(struct skc_node *node);
+const char * __init skc_node_get_data(struct skc_node *node);
+
+/**
+ * skc_node_is_value() - Test the node is a value node
+ * @node: An SKC node.
+ *
+ * Test the @node is a value node and return true if a value node, false if not.
+ */
+static inline __init bool skc_node_is_value(struct skc_node *node)
+{
+ return !!(node->data & SKC_VALUE);
+}
+
+/**
+ * skc_node_is_key() - Test the node is a key node
+ * @node: An SKC node.
+ *
+ * Test the @node is a key node and return true if a key node, false if not.
+ */
+static inline __init bool skc_node_is_key(struct skc_node *node)
+{
+ return !(node->data & SKC_VALUE);
+}
+
+/**
+ * skc_node_is_array() - Test the node is an arraied value node
+ * @node: An SKC node.
+ *
+ * Test the @node is an arraied value node.
+ */
+static inline __init bool skc_node_is_array(struct skc_node *node)
+{
+ return skc_node_is_value(node) && node->next != 0;
+}
+
+/**
+ * skc_node_is_leaf() - Test the node is a leaf key node
+ * @node: An SKC node.
+ *
+ * Test the @node is a leaf key node which is a key node and has a value node
+ * or no child. Returns true if it is a leaf node, or false if not.
+ */
+static inline __init bool skc_node_is_leaf(struct skc_node *node)
+{
+ return skc_node_is_key(node) &&
+ (!node->child || skc_node_is_value(skc_node_get_child(node)));
+}
+
+/* Tree-based key-value access APIs */
+struct skc_node * __init skc_node_find_child(struct skc_node *parent,
+ const char *key);
+
+const char * __init skc_node_find_value(struct skc_node *parent,
+ const char *key,
+ struct skc_node **vnode);
+
+struct skc_node * __init skc_node_find_next_leaf(struct skc_node *root,
+ struct skc_node *leaf);
+
+const char * __init skc_node_find_next_key_value(struct skc_node *root,
+ struct skc_node **leaf);
+
+/**
+ * skc_find_value() - Find a value which matches the key
+ * @key: Search key
+ * @vnode: A container pointer of SKC value node.
+ *
+ * Search a value whose key matches @key from whole of SKC tree and return
+ * the value if found. Found value node is stored in *@vnode.
+ * Note that this can return 0-length string and store NULL in *@vnode for
+ * key-only (non-value) entry.
+ */
+static inline const char * __init
+skc_find_value(const char *key, struct skc_node **vnode)
+{
+ return skc_node_find_value(NULL, key, vnode);
+}
+
+/**
+ * skc_find_node() - Find a node which matches the key
+ * @key: Search key
+ * @value: A container pointer of SKC value node.
+ *
+ * Search a (key) node whose key matches @key from whole of SKC tree and
+ * return the node if found. If not found, returns NULL.
+ */
+static inline struct skc_node * __init skc_find_node(const char *key)
+{
+ return skc_node_find_child(NULL, key);
+}
+
+/**
+ * skc_array_for_each_value() - Iterate value nodes on an array
+ * @anode: An SKC arraied value node
+ * @value: A value
+ *
+ * Iterate array value nodes and values starts from @anode. This is expected to
+ * be used with skc_find_value() and skc_node_find_value(), so that user can
+ * process each array entry node.
+ */
+#define skc_array_for_each_value(anode, value) \
+ for (value = skc_node_get_data(anode); anode != NULL ; \
+ anode = skc_node_get_next(anode), \
+ value = anode ? skc_node_get_data(anode) : NULL)
+
+/**
+ * skc_node_for_each_child() - Iterate child nodes
+ * @parent: An SKC node.
+ * @child: Iterated SKC node.
+ *
+ * Iterate child nodes of @parent. Each child nodes are stored to @child.
+ */
+#define skc_node_for_each_child(parent, child) \
+ for (child = skc_node_get_child(parent); child != NULL ; \
+ child = skc_node_get_next(child))
+
+/**
+ * skc_node_for_each_array_value() - Iterate array entries of geven key
+ * @node: An SKC node.
+ * @key: A key string searched under @node
+ * @anode: Iterated SKC node of array entry.
+ * @value: Iterated value of array entry.
+ *
+ * Iterate array entries of given @key under @node. Each array entry node
+ * is stroed to @anode and @value. If the @node doesn't have @key node,
+ * it does nothing.
+ * Note that even if the found key node has only one value (not array)
+ * this executes block once. Hoever, if the found key node has no value
+ * (key-only node), this does nothing. So don't use this for testing the
+ * key-value pair existence.
+ */
+#define skc_node_for_each_array_value(node, key, anode, value) \
+ for (value = skc_node_find_value(node, key, &anode); value != NULL; \
+ anode = skc_node_get_next(anode), \
+ value = anode ? skc_node_get_data(anode) : NULL)
+
+/**
+ * skc_node_for_each_key_value() - Iterate key-value pairs under a node
+ * @node: An SKC node.
+ * @knode: Iterated key node
+ * @value: Iterated value string
+ *
+ * Iterate key-value pairs under @node. Each key node and value string are
+ * stored in @knode and @value respectively.
+ */
+#define skc_node_for_each_key_value(node, knode, value) \
+ for (knode = NULL, value = skc_node_find_next_key_value(node, &knode);\
+ knode != NULL; value = skc_node_find_next_key_value(node, &knode))
+
+/**
+ * skc_for_each_key_value() - Iterate key-value pairs
+ * @knode: Iterated key node
+ * @value: Iterated value string
+ *
+ * Iterate key-value pairs in whole SKC tree. Each key node and value string
+ * are stored in @knode and @value respectively.
+ */
+#define skc_for_each_key_value(knode, value) \
+ skc_node_for_each_key_value(NULL, knode, value)
+
+/* Compose complete key */
+int __init skc_node_compose_key(struct skc_node *node, char *buf, size_t size);
+
+/* SKC node initializer */
+int __init skc_init(char *buf);
+
+/* Debug dump functions */
+void __init skc_debug_dump(void);
+
+#endif
diff --git a/lib/Kconfig b/lib/Kconfig
index f33d66fc0e86..97a53f400a7c 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -568,6 +568,9 @@ config DIMLIB
config LIBFDT
bool
+config LIBSKC
+ bool
+
config OID_REGISTRY
tristate
help
diff --git a/lib/Makefile b/lib/Makefile
index 29c02a924973..52132d0d8ff2 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -229,6 +229,8 @@ $(foreach file, $(libfdt_files), \
$(eval CFLAGS_$(file) = -I $(srctree)/scripts/dtc/libfdt))
lib-$(CONFIG_LIBFDT) += $(libfdt_files)
+lib-$(CONFIG_LIBSKC) += skc.o
+
obj-$(CONFIG_RBTREE_TEST) += rbtree_test.o
obj-$(CONFIG_INTERVAL_TREE_TEST) += interval_tree_test.o
diff --git a/lib/skc.c b/lib/skc.c
new file mode 100644
index 000000000000..bbcc81724ec4
--- /dev/null
+++ b/lib/skc.c
@@ -0,0 +1,694 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Supplemental Kernel Commandline
+ * Masami Hiramatsu <mhiramat@kernel.org>
+ */
+
+#define pr_fmt(fmt) "skc: " fmt
+
+#include <linux/bug.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/skc.h>
+#include <linux/string.h>
+
+/*
+ * Supplemental Kernel Commandline (SKC) is given as tree-structured ascii
+ * text of key-value pairs on memory.
+ * skc_parse() parses the text to build a simple tree. Each tree node is
+ * simply a key word or a value. A key node may have a next key node or/and
+ * a child node (both key and value). A value node may have a next value
+ * node (for array).
+ */
+
+static struct skc_node skc_nodes[SKC_NODE_MAX] __initdata;
+static int skc_node_num __initdata;
+static char *skc_data __initdata;
+static size_t skc_data_size __initdata;
+static struct skc_node *last_parent __initdata;
+
+static int __init skc_parse_error(const char *msg, const char *p)
+{
+ int line = 0, col = 0;
+ int i, pos = p - skc_data;
+
+ for (i = 0; i < pos; i++) {
+ if (skc_data[i] == '\n') {
+ line++;
+ col = pos - i;
+ }
+ }
+ pr_err("Parse error at line %d, col %d: %s\n", line + 1, col + 1, msg);
+ return -EINVAL;
+}
+
+/**
+ * skc_root_node() - Get the root node of supplemental kernel cmdline
+ *
+ * Return the address of root node of supplemental kernel cmdline. If the
+ * supplemental kernel cmdline is not initiized, return NULL.
+ */
+struct skc_node * __init skc_root_node(void)
+{
+ if (unlikely(!skc_data))
+ return NULL;
+
+ return skc_nodes;
+}
+
+/**
+ * skc_node_index() - Get the index of SKC node
+ * @node: A target node of getting index.
+ *
+ * Return the index number of @node in SKC node list.
+ */
+int __init skc_node_index(struct skc_node *node)
+{
+ return node - &skc_nodes[0];
+}
+
+/**
+ * skc_node_get_parent() - Get the parent SKC node
+ * @node: An SKC node.
+ *
+ * Return the parent node of @node. If the node is top node of the tree,
+ * return NULL.
+ */
+struct skc_node * __init skc_node_get_parent(struct skc_node *node)
+{
+ return node->parent == SKC_NODE_MAX ? NULL : &skc_nodes[node->parent];
+}
+
+/**
+ * skc_node_get_child() - Get the child SKC node
+ * @node: An SKC node.
+ *
+ * Return the first child node of @node. If the node has no child, return
+ * NULL.
+ */
+struct skc_node * __init skc_node_get_child(struct skc_node *node)
+{
+ return node->child ? &skc_nodes[node->child] : NULL;
+}
+
+/**
+ * skc_node_get_next() - Get the next sibling SKC node
+ * @node: An SKC node.
+ *
+ * Return the NEXT sibling node of @node. If the node has no next sibling,
+ * return NULL. Note that even if this returns NULL, it doesn't mean @node
+ * has no siblings. (You also has to check whether the parent's child node
+ * is @node or not.)
+ */
+struct skc_node * __init skc_node_get_next(struct skc_node *node)
+{
+ return node->next ? &skc_nodes[node->next] : NULL;
+}
+
+/**
+ * skc_node_get_data() - Get the data of SKC node
+ * @node: An SKC node.
+ *
+ * Return the data (which is always a null terminated string) of @node.
+ * If the node has invalid data, warn and return NULL.
+ */
+const char * __init skc_node_get_data(struct skc_node *node)
+{
+ int offset = node->data & ~SKC_VALUE;
+
+ if (WARN_ON(offset >= skc_data_size))
+ return NULL;
+
+ return skc_data + offset;
+}
+
+static bool __init
+skc_node_match_prefix(struct skc_node *node, const char **prefix)
+{
+ const char *p = skc_node_get_data(node);
+ int len = strlen(p);
+
+ if (strncmp(*prefix, p, len))
+ return false;
+
+ p = *prefix + len;
+ if (*p == '.')
+ p++;
+ else if (*p != '\0')
+ return false;
+ *prefix = p;
+
+ return true;
+}
+
+/**
+ * skc_node_find_child() - Find a child node which matches given key
+ * @parent: An SKC node.
+ * @key: A key string.
+ *
+ * Search a node under @parent which matches @key. The @key can contain
+ * several words jointed with '.'. If @parent is NULL, this searches the
+ * node from whole tree. Return NULL if no node is matched.
+ */
+struct skc_node * __init
+skc_node_find_child(struct skc_node *parent, const char *key)
+{
+ struct skc_node *node;
+
+ if (parent)
+ node = skc_node_get_child(parent);
+ else
+ node = skc_root_node();
+
+ while (node && skc_node_is_key(node)) {
+ if (!skc_node_match_prefix(node, &key))
+ node = skc_node_get_next(node);
+ else if (*key != '\0')
+ node = skc_node_get_child(node);
+ else
+ break;
+ }
+
+ return node;
+}
+
+/**
+ * skc_node_find_value() - Find a value node which matches given key
+ * @parent: An SKC node.
+ * @key: A key string.
+ * @vnode: A container pointer of found SKC node.
+ *
+ * Search a value node under @parent whose (parent) key node matches @key,
+ * store it in *@vnode, and returns the value string.
+ * The @key can contain several words jointed with '.'. If @parent is NULL,
+ * this searches the node from whole tree. Return the value string if a
+ * matched key found, return NULL if no node is matched.
+ * Note that this returns 0-length string and stores NULL in *@vnode if the
+ * key has no value. And also it will return the value of the first entry if
+ * the value is an array.
+ */
+const char * __init
+skc_node_find_value(struct skc_node *parent, const char *key,
+ struct skc_node **vnode)
+{
+ struct skc_node *node = skc_node_find_child(parent, key);
+
+ if (!node || !skc_node_is_key(node))
+ return NULL;
+
+ node = skc_node_get_child(node);
+ if (node && !skc_node_is_value(node))
+ return NULL;
+
+ if (vnode)
+ *vnode = node;
+
+ return node ? skc_node_get_data(node) : "";
+}
+
+/**
+ * skc_node_compose_key() - Compose key string of the SKC node
+ * @node: An SKC node.
+ * @buf: A buffer to store the key.
+ * @size: The size of the @buf.
+ *
+ * Compose the full-length key of the @node into @buf. Returns the total
+ * length of the key stored in @buf. Or returns -EINVAL if @node is NULL,
+ * and -ERANGE if the key depth is deeper than max depth.
+ */
+int __init skc_node_compose_key(struct skc_node *node, char *buf, size_t size)
+{
+ u16 keys[SKC_DEPTH_MAX];
+ int depth = 0, ret = 0, total = 0;
+
+ if (!node)
+ return -EINVAL;
+
+ if (skc_node_is_value(node))
+ node = skc_node_get_parent(node);
+
+ while (node) {
+ keys[depth++] = skc_node_index(node);
+ if (depth == SKC_DEPTH_MAX)
+ return -ERANGE;
+ node = skc_node_get_parent(node);
+ }
+
+ while (--depth >= 0) {
+ node = skc_nodes + keys[depth];
+ ret = snprintf(buf, size, "%s%s", skc_node_get_data(node),
+ depth ? "." : "");
+ if (ret < 0)
+ return ret;
+ if (ret > size) {
+ size = 0;
+ } else {
+ size -= ret;
+ buf += ret;
+ }
+ total += ret;
+ }
+
+ return total;
+}
+
+/**
+ * skc_node_find_next_leaf() - Find the next leaf node under given node
+ * @root: An SKC root node
+ * @node: An SKC node which starts from.
+ *
+ * Search the next leaf node (which means the terminal key node) of @node
+ * under @root node (including @root node itself).
+ * Return the next node or NULL if next leaf node is not found.
+ */
+struct skc_node * __init skc_node_find_next_leaf(struct skc_node *root,
+ struct skc_node *node)
+{
+ if (unlikely(!skc_data))
+ return NULL;
+
+ if (!node) { /* First try */
+ node = root;
+ if (!node)
+ node = skc_nodes;
+ } else {
+ if (node == root) /* @root was a leaf, no child node. */
+ return NULL;
+
+ while (!node->next) {
+ node = skc_node_get_parent(node);
+ if (node == root)
+ return NULL;
+ /* User passed a node which is not uder parent */
+ if (WARN_ON(!node))
+ return NULL;
+ }
+ node = skc_node_get_next(node);
+ }
+
+ while (node && !skc_node_is_leaf(node))
+ node = skc_node_get_child(node);
+
+ return node;
+}
+
+/**
+ * skc_node_find_next_key_value() - Find the next key-value pair nodes
+ * @root: An SKC root node
+ * @leaf: A container pointer of SKC node which starts from.
+ *
+ * Search the next leaf node (which means the terminal key node) of *@leaf
+ * under @root node. Returns the value and update *@leaf if next leaf node
+ * is found, or NULL if no next leaf node is found.
+ * Note that this returns 0-length string if the key has no value, or
+ * the value of the first entry if the value is an array.
+ */
+const char * __init skc_node_find_next_key_value(struct skc_node *root,
+ struct skc_node **leaf)
+{
+ /* tip must be passed */
+ if (WARN_ON(!leaf))
+ return NULL;
+
+ *leaf = skc_node_find_next_leaf(root, *leaf);
+ if (!*leaf)
+ return NULL;
+ if ((*leaf)->child)
+ return skc_node_get_data(skc_node_get_child(*leaf));
+ else
+ return ""; /* No value key */
+}
+
+/* SKC parse and tree build */
+
+static struct skc_node * __init skc_add_node(char *data, u32 flag)
+{
+ struct skc_node *node;
+ unsigned long offset;
+
+ if (skc_node_num == SKC_NODE_MAX)
+ return NULL;
+
+ node = &skc_nodes[skc_node_num++];
+ offset = data - skc_data;
+ node->data = (u16)offset;
+ if (WARN_ON(offset >= SKC_DATA_MAX))
+ return NULL;
+ node->data |= flag;
+ node->child = 0;
+ node->next = 0;
+
+ return node;
+}
+
+static inline __init struct skc_node *skc_last_sibling(struct skc_node *node)
+{
+ while (node->next)
+ node = skc_node_get_next(node);
+
+ return node;
+}
+
+static struct skc_node * __init skc_add_sibling(char *data, u32 flag)
+{
+ struct skc_node *sib, *node = skc_add_node(data, flag);
+
+ if (node) {
+ if (!last_parent) {
+ node->parent = SKC_NODE_MAX;
+ sib = skc_last_sibling(skc_nodes);
+ sib->next = skc_node_index(node);
+ } else {
+ node->parent = skc_node_index(last_parent);
+ if (!last_parent->child) {
+ last_parent->child = skc_node_index(node);
+ } else {
+ sib = skc_node_get_child(last_parent);
+ sib = skc_last_sibling(sib);
+ sib->next = skc_node_index(node);
+ }
+ }
+ }
+
+ return node;
+}
+
+static inline __init struct skc_node *skc_add_child(char *data, u32 flag)
+{
+ struct skc_node *node = skc_add_sibling(data, flag);
+
+ if (node)
+ last_parent = node;
+
+ return node;
+}
+
+static inline __init bool skc_valid_keyword(char *key)
+{
+ if (key[0] == '\0')
+ return false;
+
+ while (isalnum(*key) || *key == '-' || *key == '_')
+ key++;
+
+ return *key == '\0';
+}
+
+static inline __init char *find_ending_quote(char *p)
+{
+ do {
+ p = strchr(p + 1, '"');
+ if (!p)
+ break;
+ } while (*(p - 1) == '\\');
+
+ return p;
+}
+
+/* Return delimiter or error, no node added */
+static int __init __skc_parse_value(char **__v, char **__n)
+{
+ char *p, *v = *__v;
+ int c;
+
+ v = skip_spaces(v);
+ if (*v == '"') {
+ v++;
+ p = find_ending_quote(v);
+ if (!p)
+ return skc_parse_error("No closing quotation", v);
+ *p++ = '\0';
+ p = skip_spaces(p);
+ if (*p != ',' && *p != ';')
+ return skc_parse_error("No delimiter for value", v);
+ c = *p;
+ *p++ = '\0';
+ } else {
+ p = strpbrk(v, ",;");
+ if (!p)
+ return skc_parse_error("No delimiter for value", v);
+ c = *p;
+ *p++ = '\0';
+ v = strim(v);
+ }
+ *__v = v;
+ *__n = p;
+
+ return c;
+}
+
+static int __init skc_parse_array(char **__v)
+{
+ struct skc_node *node;
+ char *next;
+ int c = 0;
+
+ do {
+ c = __skc_parse_value(__v, &next);
+ if (c < 0)
+ return c;
+
+ node = skc_add_sibling(*__v, SKC_VALUE);
+ if (!node)
+ return -ENOMEM;
+ *__v = next;
+ } while (c != ';');
+ node->next = 0;
+
+ return 0;
+}
+
+static inline __init
+struct skc_node *find_match_node(struct skc_node *node, char *k)
+{
+ while (node) {
+ if (!strcmp(skc_node_get_data(node), k))
+ break;
+ node = skc_node_get_next(node);
+ }
+ return node;
+}
+
+static int __init __skc_add_key(char *k)
+{
+ struct skc_node *node;
+
+ if (!skc_valid_keyword(k))
+ return skc_parse_error("Invalid keyword", k);
+
+ if (unlikely(skc_node_num == 0))
+ goto add_node;
+
+ if (!last_parent) /* the first level */
+ node = find_match_node(skc_nodes, k);
+ else
+ node = find_match_node(skc_node_get_child(last_parent), k);
+
+ if (node)
+ last_parent = node;
+ else {
+add_node:
+ node = skc_add_child(k, SKC_KEY);
+ if (!node)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int __init __skc_parse_keys(char *k)
+{
+ char *p;
+ int ret;
+
+ k = strim(k);
+ while ((p = strchr(k, '.'))) {
+ *p++ = '\0';
+ ret = __skc_add_key(k);
+ if (ret)
+ return ret;
+ k = p;
+ }
+
+ return __skc_add_key(k);
+}
+
+static int __init skc_parse_kv(char **k, char *v)
+{
+ struct skc_node *prev_parent = last_parent;
+ struct skc_node *node;
+ char *next;
+ int c, ret;
+
+ ret = __skc_parse_keys(*k);
+ if (ret)
+ return ret;
+
+ c = __skc_parse_value(&v, &next);
+ if (c < 0)
+ return c;
+
+ node = skc_add_sibling(v, SKC_VALUE);
+ if (!node)
+ return -ENOMEM;
+
+ if (c == ',') { /* Array */
+ ret = skc_parse_array(&next);
+ if (ret < 0)
+ return ret;
+ }
+
+ last_parent = prev_parent;
+
+ *k = next;
+
+ return 0;
+}
+
+static int __init skc_parse_key(char **k, char *n)
+{
+ struct skc_node *prev_parent = last_parent;
+ int ret;
+
+ *k = strim(*k);
+ if (**k != '\0') {
+ ret = __skc_parse_keys(*k);
+ if (ret)
+ return ret;
+ last_parent = prev_parent;
+ }
+
+ *k = n;
+
+ return 0;
+}
+
+static int __init skc_open_brace(char **k, char *n)
+{
+ int ret;
+
+ ret = __skc_parse_keys(*k);
+ if (ret)
+ return ret;
+
+ /* Mark the last key as open brace */
+ last_parent->next = SKC_NODE_MAX;
+
+ *k = n;
+
+ return 0;
+}
+
+static int __init skc_close_brace(char **k, char *n)
+{
+ struct skc_node *node;
+
+ *k = strim(*k);
+ if (**k != '\0')
+ return skc_parse_error("Unexpected key, maybe forgot ;?", *k);
+
+ if (!last_parent || last_parent->next != SKC_NODE_MAX)
+ return skc_parse_error("Unexpected closing brace", *k);
+
+ node = last_parent;
+ node->next = 0;
+ do {
+ node = skc_node_get_parent(node);
+ } while (node && node->next != SKC_NODE_MAX);
+ last_parent = node;
+
+ *k = n;
+
+ return 0;
+}
+
+static int __init skc_verify_tree(void)
+{
+ int i;
+
+ for (i = 0; i < skc_node_num; i++) {
+ if (skc_nodes[i].next > skc_node_num) {
+ return skc_parse_error("No closing brace",
+ skc_node_get_data(skc_nodes + i));
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * skc_init() - Parse given SKC file and build SKC internal tree
+ * @buf: Supplemental kernel cmdline text
+ *
+ * This parses the supplemental kernel cmdline text in @buf. @buf must be a
+ * null terminated string and smaller than SKC_DATA_MAX.
+ * Return 0 if succeeded, or -errno if there is any error.
+ */
+int __init skc_init(char *buf)
+{
+ char *p, *q;
+ int ret, c;
+
+ if (skc_data)
+ return -EBUSY;
+
+ ret = strlen(buf);
+ if (ret > SKC_DATA_MAX - 1 || ret == 0)
+ return -ERANGE;
+
+ skc_data = buf;
+ skc_data_size = ret + 1;
+
+ p = buf;
+ do {
+ q = strpbrk(p, "{}=;");
+ if (!q)
+ break;
+ c = *q;
+ *q++ = '\0';
+ switch (c) {
+ case '=':
+ ret = skc_parse_kv(&p, q);
+ break;
+ case '{':
+ ret = skc_open_brace(&p, q);
+ break;
+ case ';':
+ ret = skc_parse_key(&p, q);
+ break;
+ case '}':
+ ret = skc_close_brace(&p, q);
+ break;
+ }
+ } while (ret == 0);
+
+ if (ret < 0)
+ return ret;
+
+ if (!q) {
+ p = skip_spaces(p);
+ if (*p != '\0')
+ return skc_parse_error("No delimiter", p);
+ }
+
+ return skc_verify_tree();
+}
+
+/**
+ * skc_debug_dump() - Dump current SKC node list
+ *
+ * Dump the current SKC node list on printk buffer for debug.
+ */
+void __init skc_debug_dump(void)
+{
+ int i;
+
+ for (i = 0; i < skc_node_num; i++) {
+ pr_debug("[%d] %s (%s) .next=%d, .child=%d .parent=%d\n", i,
+ skc_node_get_data(skc_nodes + i),
+ skc_node_is_value(skc_nodes + i) ? "value" : "key",
+ skc_nodes[i].next, skc_nodes[i].child,
+ skc_nodes[i].parent);
+ }
+}
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox