|
@@ -999,6 +999,69 @@ err_put:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+#define BPF_MAP_LOOKUP_AND_DELETE_ELEM_LAST_FIELD value
|
|
|
+
|
|
|
+static int map_lookup_and_delete_elem(union bpf_attr *attr)
|
|
|
+{
|
|
|
+ void __user *ukey = u64_to_user_ptr(attr->key);
|
|
|
+ void __user *uvalue = u64_to_user_ptr(attr->value);
|
|
|
+ int ufd = attr->map_fd;
|
|
|
+ struct bpf_map *map;
|
|
|
+ void *key, *value, *ptr;
|
|
|
+ u32 value_size;
|
|
|
+ struct fd f;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (CHECK_ATTR(BPF_MAP_LOOKUP_AND_DELETE_ELEM))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ f = fdget(ufd);
|
|
|
+ map = __bpf_map_get(f);
|
|
|
+ if (IS_ERR(map))
|
|
|
+ return PTR_ERR(map);
|
|
|
+
|
|
|
+ if (!(f.file->f_mode & FMODE_CAN_WRITE)) {
|
|
|
+ err = -EPERM;
|
|
|
+ goto err_put;
|
|
|
+ }
|
|
|
+
|
|
|
+ key = __bpf_copy_key(ukey, map->key_size);
|
|
|
+ if (IS_ERR(key)) {
|
|
|
+ err = PTR_ERR(key);
|
|
|
+ goto err_put;
|
|
|
+ }
|
|
|
+
|
|
|
+ value_size = map->value_size;
|
|
|
+
|
|
|
+ err = -ENOMEM;
|
|
|
+ value = kmalloc(value_size, GFP_USER | __GFP_NOWARN);
|
|
|
+ if (!value)
|
|
|
+ goto free_key;
|
|
|
+
|
|
|
+ if (map->map_type == BPF_MAP_TYPE_QUEUE ||
|
|
|
+ map->map_type == BPF_MAP_TYPE_STACK) {
|
|
|
+ err = map->ops->map_pop_elem(map, value);
|
|
|
+ } else {
|
|
|
+ err = -ENOTSUPP;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (err)
|
|
|
+ goto free_value;
|
|
|
+
|
|
|
+ if (copy_to_user(uvalue, value, value_size) != 0)
|
|
|
+ goto free_value;
|
|
|
+
|
|
|
+ err = 0;
|
|
|
+
|
|
|
+free_value:
|
|
|
+ kfree(value);
|
|
|
+free_key:
|
|
|
+ kfree(key);
|
|
|
+err_put:
|
|
|
+ fdput(f);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static const struct bpf_prog_ops * const bpf_prog_types[] = {
|
|
|
#define BPF_PROG_TYPE(_id, _name) \
|
|
|
[_id] = & _name ## _prog_ops,
|
|
@@ -2472,6 +2535,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
|
|
|
case BPF_TASK_FD_QUERY:
|
|
|
err = bpf_task_fd_query(&attr, uattr);
|
|
|
break;
|
|
|
+ case BPF_MAP_LOOKUP_AND_DELETE_ELEM:
|
|
|
+ err = map_lookup_and_delete_elem(&attr);
|
|
|
+ break;
|
|
|
default:
|
|
|
err = -EINVAL;
|
|
|
break;
|