libbpf: Improve LINUX_VERSION_CODE detection
authorAndrii Nakryiko <andrii@kernel.org>
Wed, 22 Dec 2021 23:10:03 +0000 (15:10 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 29 Dec 2021 03:20:31 +0000 (19:20 -0800)
Ubuntu reports incorrect kernel version through uname(), which on older
kernels leads to kprobe BPF programs failing to load due to the version
check mismatch.

Accommodate Ubuntu's quirks with LINUX_VERSION_CODE by using
Ubuntu-specific /proc/version_code to fetch major/minor/patch versions
to form LINUX_VERSION_CODE.

While at it, consolide libbpf's kernel version detection code between
libbpf.c and libbpf_probes.c.

  [0] Closes: https://github.com/libbpf/libbpf/issues/421

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Yonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/bpf/20211222231003.2334940-1-andrii@kernel.org
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf_internal.h
tools/lib/bpf/libbpf_probes.c

index cf862a1..9cb99d1 100644 (file)
@@ -795,11 +795,36 @@ bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data,
        return 0;
 }
 
-static __u32 get_kernel_version(void)
+__u32 get_kernel_version(void)
 {
+       /* On Ubuntu LINUX_VERSION_CODE doesn't correspond to info.release,
+        * but Ubuntu provides /proc/version_signature file, as described at
+        * https://ubuntu.com/kernel, with an example contents below, which we
+        * can use to get a proper LINUX_VERSION_CODE.
+        *
+        *   Ubuntu 5.4.0-12.15-generic 5.4.8
+        *
+        * In the above, 5.4.8 is what kernel is actually expecting, while
+        * uname() call will return 5.4.0 in info.release.
+        */
+       const char *ubuntu_kver_file = "/proc/version_signature";
        __u32 major, minor, patch;
        struct utsname info;
 
+       if (access(ubuntu_kver_file, R_OK) == 0) {
+               FILE *f;
+
+               f = fopen(ubuntu_kver_file, "r");
+               if (f) {
+                       if (fscanf(f, "%*s %*s %d.%d.%d\n", &major, &minor, &patch) == 3) {
+                               fclose(f);
+                               return KERNEL_VERSION(major, minor, patch);
+                       }
+                       fclose(f);
+               }
+               /* something went wrong, fall back to uname() approach */
+       }
+
        uname(&info);
        if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3)
                return 0;
index 5dbe4f4..1565679 100644 (file)
@@ -188,6 +188,8 @@ static inline void libbpf_strlcpy(char *dst, const char *src, size_t sz)
        dst[i] = '\0';
 }
 
+__u32 get_kernel_version(void);
+
 struct btf;
 struct btf_type;
 
index 9d39852..97b06ce 100644 (file)
@@ -48,22 +48,6 @@ static int get_vendor_id(int ifindex)
        return strtol(buf, NULL, 0);
 }
 
-static int get_kernel_version(void)
-{
-       int version, subversion, patchlevel;
-       struct utsname utsn;
-
-       /* Return 0 on failure, and attempt to probe with empty kversion */
-       if (uname(&utsn))
-               return 0;
-
-       if (sscanf(utsn.release, "%d.%d.%d",
-                  &version, &subversion, &patchlevel) != 3)
-               return 0;
-
-       return (version << 16) + (subversion << 8) + patchlevel;
-}
-
 static int probe_prog_load(enum bpf_prog_type prog_type,
                           const struct bpf_insn *insns, size_t insns_cnt,
                           char *log_buf, size_t log_buf_sz,