External users of the tracing_map API may customize their map usage
and need to be notified of certain events occuring during the lifetime
of a map.

One example would be element deletion - if private data has been
associated with an element's value, the client should be notified when
that element is being deleted in order to give it a chance to delete
the private data.

struct bpf_map_client_ops defines a set of client callbacks, a pointer
to an instance of which can be passed by the client to tracing_map_create().

Initially, the only callback defined is free_elem(), which is called
when a map element is freed and gives clients the opportunity to free
app-specific elements.  Other callbacks such as a client-defined
sort() function are anticipated, however, which is why it's not a
standalone op.

Signed-off-by: Tom Zanussi <tom.zanu...@linux.intel.com>
---
 include/linux/bpf.h   |  8 +++++++-
 kernel/bpf/arraymap.c | 16 ++++++++++++++++
 kernel/bpf/hashtab.c  | 12 ++++++++++++
 kernel/bpf/syscall.c  |  7 +++++--
 4 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index f7f95d7..09095ec 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -27,6 +27,10 @@ struct bpf_map_ops {
        int (*map_delete_elem)(struct bpf_map *map, void *key);
 };
 
+struct bpf_map_client_ops {
+       void (*free_elem)(void *value);
+};
+
 struct bpf_map {
        atomic_t refcnt;
        enum bpf_map_type map_type;
@@ -35,6 +39,7 @@ struct bpf_map {
        u32 max_entries;
        struct bpf_map_ops *ops;
        struct work_struct work;
+       struct bpf_map_client_ops *client_ops;
 };
 
 struct bpf_map_type_list {
@@ -143,7 +148,8 @@ extern struct bpf_func_proto bpf_map_lookup_elem_proto;
 extern struct bpf_func_proto bpf_map_update_elem_proto;
 extern struct bpf_func_proto bpf_map_delete_elem_proto;
 
-struct bpf_map *tracing_map_create(union bpf_attr *attr);
+struct bpf_map *tracing_map_create(union bpf_attr *attr,
+                                  struct bpf_map_client_ops *client_ops);
 void tracing_map_destroy(struct bpf_map *map);
 void tracing_map_clear(struct bpf_map *map);
 int tracing_map_update_elem(struct bpf_map *map, void *key, void *value,
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 9eb4d8a..333c959 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -119,6 +119,20 @@ static int array_map_delete_elem(struct bpf_map *map, void 
*key)
        return -EINVAL;
 }
 
+static void free_client_elems(struct bpf_array *array)
+{
+       void *val;
+       u32 index;
+
+       if (!array->map.client_ops || !array->map.client_ops->free_elem)
+               return;
+
+       for (index = 0; index < array->map.max_entries; index++) {
+               val = array->value + array->elem_size * index;
+               array->map.client_ops->free_elem(val);
+       }
+}
+
 /* Called when map->refcnt goes to zero, either from workqueue or from syscall 
*/
 static void array_map_free(struct bpf_map *map)
 {
@@ -131,6 +145,8 @@ static void array_map_free(struct bpf_map *map)
         */
        synchronize_rcu();
 
+       free_client_elems(array);
+
        kvfree(array);
 }
 
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index addf3a8..6f349ad 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -275,6 +275,16 @@ err:
        return ret;
 }
 
+static void free_client_elem(struct bpf_htab *htab, struct htab_elem *l)
+{
+       void *val;
+
+       if (htab->map.client_ops && htab->map.client_ops->free_elem) {
+               val = l->key + round_up(htab->map.key_size, 8);
+               htab->map.client_ops->free_elem(val);
+       }
+}
+
 /* Called from syscall or from eBPF program */
 static int htab_map_delete_elem(struct bpf_map *map, void *key)
 {
@@ -298,6 +308,7 @@ static int htab_map_delete_elem(struct bpf_map *map, void 
*key)
        l = lookup_elem_raw(head, hash, key, key_size);
 
        if (l) {
+               free_client_elem(htab, l);
                hlist_del_rcu(&l->hash_node);
                htab->count--;
                kfree_rcu(l, rcu);
@@ -318,6 +329,7 @@ static void delete_all_elements(struct bpf_htab *htab)
                struct htab_elem *l;
 
                hlist_for_each_entry_safe(l, n, head, hash_node) {
+                       free_client_elem(htab, l);
                        hlist_del_rcu(&l->hash_node);
                        htab->count--;
                        kfree(l);
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 0f28904..85735e6 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -110,7 +110,8 @@ static const struct file_operations bpf_map_fops = {
  *
  * Return: the map created on success, ERR_PTR otherwise
  */
-struct bpf_map *tracing_map_create(union bpf_attr *attr)
+struct bpf_map *tracing_map_create(union bpf_attr *attr,
+                                  struct bpf_map_client_ops *client_ops)
 {
        struct bpf_map *map;
 
@@ -119,6 +120,8 @@ struct bpf_map *tracing_map_create(union bpf_attr *attr)
        if (!IS_ERR(map))
                atomic_set(&map->refcnt, 1);
 
+       map->client_ops = client_ops;
+
        return map;
 }
 EXPORT_SYMBOL_GPL(tracing_map_create);
@@ -133,7 +136,7 @@ static int map_create(union bpf_attr *attr)
        if (err)
                return -EINVAL;
 
-       map = tracing_map_create(attr);
+       map = tracing_map_create(attr, NULL);
        if (IS_ERR(map))
                return PTR_ERR(map);
 
-- 
1.9.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to