Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost
[linux-2.6-microblaze.git] / kernel / bpf / btf.c
index f982a9f..dfe61df 100644 (file)
@@ -51,7 +51,7 @@
  * The BTF type section contains a list of 'struct btf_type' objects.
  * Each one describes a C type.  Recall from the above section
  * that a 'struct btf_type' object could be immediately followed by extra
- * data in order to desribe some particular C types.
+ * data in order to describe some particular C types.
  *
  * type_id:
  * ~~~~~~~
@@ -1143,7 +1143,7 @@ static void *btf_show_obj_safe(struct btf_show *show,
 
        /*
         * We need a new copy to our safe object, either because we haven't
-        * yet copied and are intializing safe data, or because the data
+        * yet copied and are initializing safe data, or because the data
         * we want falls outside the boundaries of the safe object.
         */
        if (!safe) {
@@ -3046,43 +3046,92 @@ static void btf_struct_log(struct btf_verifier_env *env,
        btf_verifier_log(env, "size=%u vlen=%u", t->size, btf_type_vlen(t));
 }
 
-/* find 'struct bpf_spin_lock' in map value.
- * return >= 0 offset if found
- * and < 0 in case of error
- */
-int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t)
+static int btf_find_struct_field(const struct btf *btf, const struct btf_type *t,
+                                const char *name, int sz, int align)
 {
        const struct btf_member *member;
        u32 i, off = -ENOENT;
 
-       if (!__btf_type_is_struct(t))
-               return -EINVAL;
-
        for_each_member(i, t, member) {
                const struct btf_type *member_type = btf_type_by_id(btf,
                                                                    member->type);
                if (!__btf_type_is_struct(member_type))
                        continue;
-               if (member_type->size != sizeof(struct bpf_spin_lock))
+               if (member_type->size != sz)
                        continue;
-               if (strcmp(__btf_name_by_offset(btf, member_type->name_off),
-                          "bpf_spin_lock"))
+               if (strcmp(__btf_name_by_offset(btf, member_type->name_off), name))
                        continue;
                if (off != -ENOENT)
-                       /* only one 'struct bpf_spin_lock' is allowed */
+                       /* only one such field is allowed */
                        return -E2BIG;
                off = btf_member_bit_offset(t, member);
                if (off % 8)
                        /* valid C code cannot generate such BTF */
                        return -EINVAL;
                off /= 8;
-               if (off % __alignof__(struct bpf_spin_lock))
-                       /* valid struct bpf_spin_lock will be 4 byte aligned */
+               if (off % align)
+                       return -EINVAL;
+       }
+       return off;
+}
+
+static int btf_find_datasec_var(const struct btf *btf, const struct btf_type *t,
+                               const char *name, int sz, int align)
+{
+       const struct btf_var_secinfo *vsi;
+       u32 i, off = -ENOENT;
+
+       for_each_vsi(i, t, vsi) {
+               const struct btf_type *var = btf_type_by_id(btf, vsi->type);
+               const struct btf_type *var_type = btf_type_by_id(btf, var->type);
+
+               if (!__btf_type_is_struct(var_type))
+                       continue;
+               if (var_type->size != sz)
+                       continue;
+               if (vsi->size != sz)
+                       continue;
+               if (strcmp(__btf_name_by_offset(btf, var_type->name_off), name))
+                       continue;
+               if (off != -ENOENT)
+                       /* only one such field is allowed */
+                       return -E2BIG;
+               off = vsi->offset;
+               if (off % align)
                        return -EINVAL;
        }
        return off;
 }
 
+static int btf_find_field(const struct btf *btf, const struct btf_type *t,
+                         const char *name, int sz, int align)
+{
+
+       if (__btf_type_is_struct(t))
+               return btf_find_struct_field(btf, t, name, sz, align);
+       else if (btf_type_is_datasec(t))
+               return btf_find_datasec_var(btf, t, name, sz, align);
+       return -EINVAL;
+}
+
+/* find 'struct bpf_spin_lock' in map value.
+ * return >= 0 offset if found
+ * and < 0 in case of error
+ */
+int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t)
+{
+       return btf_find_field(btf, t, "bpf_spin_lock",
+                             sizeof(struct bpf_spin_lock),
+                             __alignof__(struct bpf_spin_lock));
+}
+
+int btf_find_timer(const struct btf *btf, const struct btf_type *t)
+{
+       return btf_find_field(btf, t, "bpf_timer",
+                             sizeof(struct bpf_timer),
+                             __alignof__(struct bpf_timer));
+}
+
 static void __btf_struct_show(const struct btf *btf, const struct btf_type *t,
                              u32 type_id, void *data, u8 bits_offset,
                              struct btf_show *show)
@@ -3417,7 +3466,7 @@ static struct btf_kind_operations func_proto_ops = {
         * BTF_KIND_FUNC_PROTO cannot be directly referred by
         * a struct's member.
         *
-        * It should be a funciton pointer instead.
+        * It should be a function pointer instead.
         * (i.e. struct's member -> BTF_KIND_PTR -> BTF_KIND_FUNC_PROTO)
         *
         * Hence, there is no btf_func_check_member().
@@ -4257,7 +4306,7 @@ static int btf_parse_hdr(struct btf_verifier_env *env)
        return 0;
 }
 
-static struct btf *btf_parse(void __user *btf_data, u32 btf_data_size,
+static struct btf *btf_parse(bpfptr_t btf_data, u32 btf_data_size,
                             u32 log_level, char __user *log_ubuf, u32 log_size)
 {
        struct btf_verifier_env *env = NULL;
@@ -4306,7 +4355,7 @@ static struct btf *btf_parse(void __user *btf_data, u32 btf_data_size,
        btf->data = data;
        btf->data_size = btf_data_size;
 
-       if (copy_from_user(data, btf_data, btf_data_size)) {
+       if (copy_from_bpfptr(data, btf_data, btf_data_size)) {
                err = -EFAULT;
                goto errout;
        }
@@ -4776,6 +4825,11 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
                const struct bpf_ctx_arg_aux *ctx_arg_info = &prog->aux->ctx_arg_info[i];
 
                if (ctx_arg_info->offset == off) {
+                       if (!ctx_arg_info->btf_id) {
+                               bpf_log(log,"invalid btf_id for context argument offset %u\n", off);
+                               return false;
+                       }
+
                        info->reg_type = ctx_arg_info->reg_type;
                        info->btf = btf_vmlinux;
                        info->btf_id = ctx_arg_info->btf_id;
@@ -5792,12 +5846,12 @@ static int __btf_new_fd(struct btf *btf)
        return anon_inode_getfd("btf", &btf_fops, btf, O_RDONLY | O_CLOEXEC);
 }
 
-int btf_new_fd(const union bpf_attr *attr)
+int btf_new_fd(const union bpf_attr *attr, bpfptr_t uattr)
 {
        struct btf *btf;
        int ret;
 
-       btf = btf_parse(u64_to_user_ptr(attr->btf),
+       btf = btf_parse(make_bpfptr(attr->btf, uattr.is_kernel),
                        attr->btf_size, attr->btf_log_level,
                        u64_to_user_ptr(attr->btf_log_buf),
                        attr->btf_log_size);
@@ -6097,3 +6151,67 @@ struct module *btf_try_get_module(const struct btf *btf)
 
        return res;
 }
+
+BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int, flags)
+{
+       struct btf *btf;
+       long ret;
+
+       if (flags)
+               return -EINVAL;
+
+       if (name_sz <= 1 || name[name_sz - 1])
+               return -EINVAL;
+
+       btf = bpf_get_btf_vmlinux();
+       if (IS_ERR(btf))
+               return PTR_ERR(btf);
+
+       ret = btf_find_by_name_kind(btf, name, kind);
+       /* ret is never zero, since btf_find_by_name_kind returns
+        * positive btf_id or negative error.
+        */
+       if (ret < 0) {
+               struct btf *mod_btf;
+               int id;
+
+               /* If name is not found in vmlinux's BTF then search in module's BTFs */
+               spin_lock_bh(&btf_idr_lock);
+               idr_for_each_entry(&btf_idr, mod_btf, id) {
+                       if (!btf_is_module(mod_btf))
+                               continue;
+                       /* linear search could be slow hence unlock/lock
+                        * the IDR to avoiding holding it for too long
+                        */
+                       btf_get(mod_btf);
+                       spin_unlock_bh(&btf_idr_lock);
+                       ret = btf_find_by_name_kind(mod_btf, name, kind);
+                       if (ret > 0) {
+                               int btf_obj_fd;
+
+                               btf_obj_fd = __btf_new_fd(mod_btf);
+                               if (btf_obj_fd < 0) {
+                                       btf_put(mod_btf);
+                                       return btf_obj_fd;
+                               }
+                               return ret | (((u64)btf_obj_fd) << 32);
+                       }
+                       spin_lock_bh(&btf_idr_lock);
+                       btf_put(mod_btf);
+               }
+               spin_unlock_bh(&btf_idr_lock);
+       }
+       return ret;
+}
+
+const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {
+       .func           = bpf_btf_find_by_name_kind,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_MEM,
+       .arg2_type      = ARG_CONST_SIZE,
+       .arg3_type      = ARG_ANYTHING,
+       .arg4_type      = ARG_ANYTHING,
+};
+
+BTF_ID_LIST_GLOBAL_SINGLE(btf_task_struct_ids, struct, task_struct)