[PATCH 3/5] ebpf: add formal error reporting to all APIs

Daniel P. Berrangé posted 5 patches 3 months, 2 weeks ago
There is a newer version of this series
[PATCH 3/5] ebpf: add formal error reporting to all APIs
Posted by Daniel P. Berrangé 3 months, 2 weeks ago
The eBPF code is currently reporting error messages through trace
events. Trace events are fine for debugging, but they are not to be
considered the primary error reporting mechanism, as their output
is inaccessible to callers.

This adds an "Error **errp" parameter to all methods which have
important error scenarios to report to the caller.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 ebpf/ebpf_rss.c     | 59 ++++++++++++++++++++++++++++++++++++---------
 ebpf/ebpf_rss.h     | 10 +++++---
 hw/net/virtio-net.c |  7 +++---
 3 files changed, 59 insertions(+), 17 deletions(-)

diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c
index aa7170d997..59854c8b51 100644
--- a/ebpf/ebpf_rss.c
+++ b/ebpf/ebpf_rss.c
@@ -47,13 +47,14 @@ bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
     return ctx != NULL && (ctx->obj != NULL || ctx->program_fd != -1);
 }
 
-static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
+static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx, Error **errp)
 {
     ctx->mmap_configuration = mmap(NULL, qemu_real_host_page_size(),
                                    PROT_READ | PROT_WRITE, MAP_SHARED,
                                    ctx->map_configuration, 0);
     if (ctx->mmap_configuration == MAP_FAILED) {
         trace_ebpf_error("eBPF RSS", "can not mmap eBPF configuration array");
+        error_setg(errp, "Unable to map eBPF configuration array");
         return false;
     }
     ctx->mmap_toeplitz_key = mmap(NULL, qemu_real_host_page_size(),
@@ -61,6 +62,7 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
                                    ctx->map_toeplitz_key, 0);
     if (ctx->mmap_toeplitz_key == MAP_FAILED) {
         trace_ebpf_error("eBPF RSS", "can not mmap eBPF toeplitz key");
+        error_setg(errp, "Unable to map eBPF toeplitz array");
         goto toeplitz_fail;
     }
     ctx->mmap_indirections_table = mmap(NULL, qemu_real_host_page_size(),
@@ -68,6 +70,7 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
                                    ctx->map_indirections_table, 0);
     if (ctx->mmap_indirections_table == MAP_FAILED) {
         trace_ebpf_error("eBPF RSS", "can not mmap eBPF indirection table");
+        error_setg(errp, "Unable to map eBPF indirection array");
         goto indirection_fail;
     }
 
@@ -95,7 +98,7 @@ static void ebpf_rss_munmap(struct EBPFRSSContext *ctx)
     ctx->mmap_indirections_table = NULL;
 }
 
-bool ebpf_rss_load(struct EBPFRSSContext *ctx)
+bool ebpf_rss_load(struct EBPFRSSContext *ctx, Error **errp)
 {
     struct rss_bpf *rss_bpf_ctx;
 
@@ -106,6 +109,7 @@ bool ebpf_rss_load(struct EBPFRSSContext *ctx)
     rss_bpf_ctx = rss_bpf__open();
     if (rss_bpf_ctx == NULL) {
         trace_ebpf_error("eBPF RSS", "can not open eBPF RSS object");
+        error_setg(errp, "Unable to open eBPF RSS object");
         goto error;
     }
 
@@ -113,6 +117,7 @@ bool ebpf_rss_load(struct EBPFRSSContext *ctx)
 
     if (rss_bpf__load(rss_bpf_ctx)) {
         trace_ebpf_error("eBPF RSS", "can not load RSS program");
+        error_setg(errp, "Unable to load eBPF program");
         goto error;
     }
 
@@ -126,7 +131,7 @@ bool ebpf_rss_load(struct EBPFRSSContext *ctx)
     ctx->map_toeplitz_key = bpf_map__fd(
             rss_bpf_ctx->maps.tap_rss_map_toeplitz_key);
 
-    if (!ebpf_rss_mmap(ctx)) {
+    if (!ebpf_rss_mmap(ctx, errp)) {
         goto error;
     }
 
@@ -143,13 +148,28 @@ error:
 }
 
 bool ebpf_rss_load_fds(struct EBPFRSSContext *ctx, int program_fd,
-                       int config_fd, int toeplitz_fd, int table_fd)
+                       int config_fd, int toeplitz_fd, int table_fd,
+                       Error **errp)
 {
     if (ebpf_rss_is_loaded(ctx)) {
+        error_setg(errp, "eBPF program is already loaded");
         return false;
     }
 
-    if (program_fd < 0 || config_fd < 0 || toeplitz_fd < 0 || table_fd < 0) {
+    if (program_fd < 0) {
+        error_setg(errp, "eBPF program FD is not open");
+        return false;
+    }
+    if (config_fd < 0) {
+        error_setg(errp, "eBPF config FD is not open");
+        return false;
+    }
+    if (toeplitz_fd < 0) {
+        error_setg(errp, "eBPF toeplitz FD is not open");
+        return false;
+    }
+    if (table_fd < 0) {
+        error_setg(errp, "eBPF indirection FD is not open");
         return false;
     }
 
@@ -158,7 +178,7 @@ bool ebpf_rss_load_fds(struct EBPFRSSContext *ctx, int program_fd,
     ctx->map_toeplitz_key = toeplitz_fd;
     ctx->map_indirections_table = table_fd;
 
-    if (!ebpf_rss_mmap(ctx)) {
+    if (!ebpf_rss_mmap(ctx, errp)) {
         ctx->program_fd = -1;
         ctx->map_configuration = -1;
         ctx->map_toeplitz_key = -1;
@@ -177,11 +197,14 @@ static void ebpf_rss_set_config(struct EBPFRSSContext *ctx,
 
 static bool ebpf_rss_set_indirections_table(struct EBPFRSSContext *ctx,
                                             uint16_t *indirections_table,
-                                            size_t len)
+                                            size_t len,
+                                            Error **errp)
 {
     char *cursor = ctx->mmap_indirections_table;
 
     if (len > VIRTIO_NET_RSS_MAX_TABLE_LEN) {
+        error_setg(errp, "Indirections table length %zu exceeds limit %d",
+                   len, VIRTIO_NET_RSS_MAX_TABLE_LEN);
         return false;
     }
 
@@ -206,17 +229,31 @@ static void ebpf_rss_set_toepliz_key(struct EBPFRSSContext *ctx,
 }
 
 bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config,
-                      uint16_t *indirections_table, uint8_t *toeplitz_key)
+                      uint16_t *indirections_table, uint8_t *toeplitz_key,
+                      Error **errp)
 {
-    if (!ebpf_rss_is_loaded(ctx) || config == NULL ||
-        indirections_table == NULL || toeplitz_key == NULL) {
+    if (!ebpf_rss_is_loaded(ctx)) {
+        error_setg(errp, "eBPF program is not loaded");
+        return false;
+    }
+    if (config == NULL) {
+        error_setg(errp, "eBPF config table is NULL");
+        return false;
+    }
+    if (indirections_table == NULL) {
+        error_setg(errp, "eBPF indirections table is NULL");
+        return false;
+    }
+    if (toeplitz_key == NULL) {
+        error_setg(errp, "eBPF toeplitz key is NULL");
         return false;
     }
 
     ebpf_rss_set_config(ctx, config);
 
     if (!ebpf_rss_set_indirections_table(ctx, indirections_table,
-                                      config->indirections_len)) {
+                                         config->indirections_len,
+                                         errp)) {
         return false;
     }
 
diff --git a/ebpf/ebpf_rss.h b/ebpf/ebpf_rss.h
index 239242b0d2..86a5787789 100644
--- a/ebpf/ebpf_rss.h
+++ b/ebpf/ebpf_rss.h
@@ -14,6 +14,8 @@
 #ifndef QEMU_EBPF_RSS_H
 #define QEMU_EBPF_RSS_H
 
+#include "qapi/error.h"
+
 #define EBPF_RSS_MAX_FDS 4
 
 struct EBPFRSSContext {
@@ -41,13 +43,15 @@ void ebpf_rss_init(struct EBPFRSSContext *ctx);
 
 bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx);
 
-bool ebpf_rss_load(struct EBPFRSSContext *ctx);
+bool ebpf_rss_load(struct EBPFRSSContext *ctx, Error **errp);
 
 bool ebpf_rss_load_fds(struct EBPFRSSContext *ctx, int program_fd,
-                       int config_fd, int toeplitz_fd, int table_fd);
+                       int config_fd, int toeplitz_fd, int table_fd,
+                       Error **errp);
 
 bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config,
-                      uint16_t *indirections_table, uint8_t *toeplitz_key);
+                      uint16_t *indirections_table, uint8_t *toeplitz_key,
+                      Error **errp);
 
 void ebpf_rss_unload(struct EBPFRSSContext *ctx);
 
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index e5ff3e4b18..85323322ea 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -1264,7 +1264,8 @@ static bool virtio_net_attach_ebpf_rss(VirtIONet *n)
     rss_data_to_rss_config(&n->rss_data, &config);
 
     if (!ebpf_rss_set_all(&n->ebpf_rss, &config,
-                          n->rss_data.indirections_table, n->rss_data.key)) {
+                          n->rss_data.indirections_table, n->rss_data.key,
+                          NULL)) {
         return false;
     }
 
@@ -1335,7 +1336,7 @@ static bool virtio_net_load_ebpf_fds(VirtIONet *n)
         }
     }
 
-    ret = ebpf_rss_load_fds(&n->ebpf_rss, fds[0], fds[1], fds[2], fds[3]);
+    ret = ebpf_rss_load_fds(&n->ebpf_rss, fds[0], fds[1], fds[2], fds[3], NULL);
 
 exit:
     if (!ret) {
@@ -1353,7 +1354,7 @@ static bool virtio_net_load_ebpf(VirtIONet *n)
 
     if (virtio_net_attach_ebpf_to_backend(n->nic, -1)) {
         if (!(n->ebpf_rss_fds && virtio_net_load_ebpf_fds(n))) {
-            ret = ebpf_rss_load(&n->ebpf_rss);
+            ret = ebpf_rss_load(&n->ebpf_rss, NULL);
         }
     }
 
-- 
2.45.2


Re: [PATCH 3/5] ebpf: add formal error reporting to all APIs
Posted by Philippe Mathieu-Daudé 3 months, 2 weeks ago
On 6/8/24 16:56, Daniel P. Berrangé wrote:
> The eBPF code is currently reporting error messages through trace
> events. Trace events are fine for debugging, but they are not to be
> considered the primary error reporting mechanism, as their output
> is inaccessible to callers.
> 
> This adds an "Error **errp" parameter to all methods which have
> important error scenarios to report to the caller.
> 
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>   ebpf/ebpf_rss.c     | 59 ++++++++++++++++++++++++++++++++++++---------
>   ebpf/ebpf_rss.h     | 10 +++++---
>   hw/net/virtio-net.c |  7 +++---
>   3 files changed, 59 insertions(+), 17 deletions(-)
> 
> diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c
> index aa7170d997..59854c8b51 100644
> --- a/ebpf/ebpf_rss.c
> +++ b/ebpf/ebpf_rss.c
> @@ -47,13 +47,14 @@ bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
>       return ctx != NULL && (ctx->obj != NULL || ctx->program_fd != -1);
>   }
>   
> -static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
> +static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx, Error **errp)
>   {
>       ctx->mmap_configuration = mmap(NULL, qemu_real_host_page_size(),
>                                      PROT_READ | PROT_WRITE, MAP_SHARED,
>                                      ctx->map_configuration, 0);
>       if (ctx->mmap_configuration == MAP_FAILED) {
>           trace_ebpf_error("eBPF RSS", "can not mmap eBPF configuration array");
> +        error_setg(errp, "Unable to map eBPF configuration array");
>           return false;
>       }
>       ctx->mmap_toeplitz_key = mmap(NULL, qemu_real_host_page_size(),
> @@ -61,6 +62,7 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
>                                      ctx->map_toeplitz_key, 0);
>       if (ctx->mmap_toeplitz_key == MAP_FAILED) {
>           trace_ebpf_error("eBPF RSS", "can not mmap eBPF toeplitz key");
> +        error_setg(errp, "Unable to map eBPF toeplitz array");
>           goto toeplitz_fail;
>       }
>       ctx->mmap_indirections_table = mmap(NULL, qemu_real_host_page_size(),
> @@ -68,6 +70,7 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
>                                      ctx->map_indirections_table, 0);
>       if (ctx->mmap_indirections_table == MAP_FAILED) {
>           trace_ebpf_error("eBPF RSS", "can not mmap eBPF indirection table");
> +        error_setg(errp, "Unable to map eBPF indirection array");

Aren't these trace_ebpf_error() calls redundant now?

>           goto indirection_fail;
>       }


Re: [PATCH 3/5] ebpf: add formal error reporting to all APIs
Posted by Daniel P. Berrangé 3 months, 2 weeks ago
On Tue, Aug 06, 2024 at 05:11:55PM +0200, Philippe Mathieu-Daudé wrote:
> On 6/8/24 16:56, Daniel P. Berrangé wrote:
> > The eBPF code is currently reporting error messages through trace
> > events. Trace events are fine for debugging, but they are not to be
> > considered the primary error reporting mechanism, as their output
> > is inaccessible to callers.
> > 
> > This adds an "Error **errp" parameter to all methods which have
> > important error scenarios to report to the caller.
> > 
> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> > ---
> >   ebpf/ebpf_rss.c     | 59 ++++++++++++++++++++++++++++++++++++---------
> >   ebpf/ebpf_rss.h     | 10 +++++---
> >   hw/net/virtio-net.c |  7 +++---
> >   3 files changed, 59 insertions(+), 17 deletions(-)
> > 
> > diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c
> > index aa7170d997..59854c8b51 100644
> > --- a/ebpf/ebpf_rss.c
> > +++ b/ebpf/ebpf_rss.c
> > @@ -47,13 +47,14 @@ bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
> >       return ctx != NULL && (ctx->obj != NULL || ctx->program_fd != -1);
> >   }
> > -static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
> > +static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx, Error **errp)
> >   {
> >       ctx->mmap_configuration = mmap(NULL, qemu_real_host_page_size(),
> >                                      PROT_READ | PROT_WRITE, MAP_SHARED,
> >                                      ctx->map_configuration, 0);
> >       if (ctx->mmap_configuration == MAP_FAILED) {
> >           trace_ebpf_error("eBPF RSS", "can not mmap eBPF configuration array");
> > +        error_setg(errp, "Unable to map eBPF configuration array");
> >           return false;
> >       }
> >       ctx->mmap_toeplitz_key = mmap(NULL, qemu_real_host_page_size(),
> > @@ -61,6 +62,7 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
> >                                      ctx->map_toeplitz_key, 0);
> >       if (ctx->mmap_toeplitz_key == MAP_FAILED) {
> >           trace_ebpf_error("eBPF RSS", "can not mmap eBPF toeplitz key");
> > +        error_setg(errp, "Unable to map eBPF toeplitz array");
> >           goto toeplitz_fail;
> >       }
> >       ctx->mmap_indirections_table = mmap(NULL, qemu_real_host_page_size(),
> > @@ -68,6 +70,7 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
> >                                      ctx->map_indirections_table, 0);
> >       if (ctx->mmap_indirections_table == MAP_FAILED) {
> >           trace_ebpf_error("eBPF RSS", "can not mmap eBPF indirection table");
> > +        error_setg(errp, "Unable to map eBPF indirection array");
> 
> Aren't these trace_ebpf_error() calls redundant now?

Yes & no. Errors propagated up the call stack don't get included in
any trace output, and so if a caller doesn't log them anywhere they
can thus be invisible to someone just looking at trace output.

I could remove them all from the eBPF code though, and put a single
trace event in the hw/net/virtio-net.c file instead ? Bit of a bike
shed colouring exercise to decide which is best though.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


Re: [PATCH 3/5] ebpf: add formal error reporting to all APIs
Posted by Philippe Mathieu-Daudé 3 months, 2 weeks ago
On 6/8/24 17:20, Daniel P. Berrangé wrote:
> On Tue, Aug 06, 2024 at 05:11:55PM +0200, Philippe Mathieu-Daudé wrote:
>> On 6/8/24 16:56, Daniel P. Berrangé wrote:
>>> The eBPF code is currently reporting error messages through trace
>>> events. Trace events are fine for debugging, but they are not to be
>>> considered the primary error reporting mechanism, as their output
>>> is inaccessible to callers.
>>>
>>> This adds an "Error **errp" parameter to all methods which have
>>> important error scenarios to report to the caller.
>>>
>>> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>>> ---
>>>    ebpf/ebpf_rss.c     | 59 ++++++++++++++++++++++++++++++++++++---------
>>>    ebpf/ebpf_rss.h     | 10 +++++---
>>>    hw/net/virtio-net.c |  7 +++---
>>>    3 files changed, 59 insertions(+), 17 deletions(-)
>>>
>>> diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c
>>> index aa7170d997..59854c8b51 100644
>>> --- a/ebpf/ebpf_rss.c
>>> +++ b/ebpf/ebpf_rss.c
>>> @@ -47,13 +47,14 @@ bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
>>>        return ctx != NULL && (ctx->obj != NULL || ctx->program_fd != -1);
>>>    }
>>> -static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
>>> +static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx, Error **errp)
>>>    {
>>>        ctx->mmap_configuration = mmap(NULL, qemu_real_host_page_size(),
>>>                                       PROT_READ | PROT_WRITE, MAP_SHARED,
>>>                                       ctx->map_configuration, 0);
>>>        if (ctx->mmap_configuration == MAP_FAILED) {
>>>            trace_ebpf_error("eBPF RSS", "can not mmap eBPF configuration array");
>>> +        error_setg(errp, "Unable to map eBPF configuration array");
>>>            return false;
>>>        }
>>>        ctx->mmap_toeplitz_key = mmap(NULL, qemu_real_host_page_size(),
>>> @@ -61,6 +62,7 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
>>>                                       ctx->map_toeplitz_key, 0);
>>>        if (ctx->mmap_toeplitz_key == MAP_FAILED) {
>>>            trace_ebpf_error("eBPF RSS", "can not mmap eBPF toeplitz key");
>>> +        error_setg(errp, "Unable to map eBPF toeplitz array");
>>>            goto toeplitz_fail;
>>>        }
>>>        ctx->mmap_indirections_table = mmap(NULL, qemu_real_host_page_size(),
>>> @@ -68,6 +70,7 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
>>>                                       ctx->map_indirections_table, 0);
>>>        if (ctx->mmap_indirections_table == MAP_FAILED) {
>>>            trace_ebpf_error("eBPF RSS", "can not mmap eBPF indirection table");
>>> +        error_setg(errp, "Unable to map eBPF indirection array");
>>
>> Aren't these trace_ebpf_error() calls redundant now?
> 
> Yes & no. Errors propagated up the call stack don't get included in
> any trace output, and so if a caller doesn't log them anywhere they
> can thus be invisible to someone just looking at trace output.
> 
> I could remove them all from the eBPF code though, and put a single
> trace event in the hw/net/virtio-net.c file instead ? Bit of a bike
> shed colouring exercise to decide which is best though.

No problem, I'm fine with this patch.

Note from experience (although pre-existing in this patch), trace
events can be very verbose, and a what makes them powerful is we
can filter particular ones. The following pattern isn't practical
to filter:

   trace_foo_error(const char *error_msg);

While a bit tedious to add, having a single trace event per error
is way more useful.

Regards,

Phil.

Re: [PATCH 3/5] ebpf: add formal error reporting to all APIs
Posted by Daniel P. Berrangé 3 months, 2 weeks ago
On Wed, Aug 07, 2024 at 09:37:00AM +0200, Philippe Mathieu-Daudé wrote:
> On 6/8/24 17:20, Daniel P. Berrangé wrote:
> > On Tue, Aug 06, 2024 at 05:11:55PM +0200, Philippe Mathieu-Daudé wrote:
> > > On 6/8/24 16:56, Daniel P. Berrangé wrote:
> > > > The eBPF code is currently reporting error messages through trace
> > > > events. Trace events are fine for debugging, but they are not to be
> > > > considered the primary error reporting mechanism, as their output
> > > > is inaccessible to callers.
> > > > 
> > > > This adds an "Error **errp" parameter to all methods which have
> > > > important error scenarios to report to the caller.
> > > > 
> > > > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> > > > ---
> > > >    ebpf/ebpf_rss.c     | 59 ++++++++++++++++++++++++++++++++++++---------
> > > >    ebpf/ebpf_rss.h     | 10 +++++---
> > > >    hw/net/virtio-net.c |  7 +++---
> > > >    3 files changed, 59 insertions(+), 17 deletions(-)
> > > > 
> > > > diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c
> > > > index aa7170d997..59854c8b51 100644
> > > > --- a/ebpf/ebpf_rss.c
> > > > +++ b/ebpf/ebpf_rss.c
> > > > @@ -47,13 +47,14 @@ bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
> > > >        return ctx != NULL && (ctx->obj != NULL || ctx->program_fd != -1);
> > > >    }
> > > > -static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
> > > > +static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx, Error **errp)
> > > >    {
> > > >        ctx->mmap_configuration = mmap(NULL, qemu_real_host_page_size(),
> > > >                                       PROT_READ | PROT_WRITE, MAP_SHARED,
> > > >                                       ctx->map_configuration, 0);
> > > >        if (ctx->mmap_configuration == MAP_FAILED) {
> > > >            trace_ebpf_error("eBPF RSS", "can not mmap eBPF configuration array");
> > > > +        error_setg(errp, "Unable to map eBPF configuration array");
> > > >            return false;
> > > >        }
> > > >        ctx->mmap_toeplitz_key = mmap(NULL, qemu_real_host_page_size(),
> > > > @@ -61,6 +62,7 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
> > > >                                       ctx->map_toeplitz_key, 0);
> > > >        if (ctx->mmap_toeplitz_key == MAP_FAILED) {
> > > >            trace_ebpf_error("eBPF RSS", "can not mmap eBPF toeplitz key");
> > > > +        error_setg(errp, "Unable to map eBPF toeplitz array");
> > > >            goto toeplitz_fail;
> > > >        }
> > > >        ctx->mmap_indirections_table = mmap(NULL, qemu_real_host_page_size(),
> > > > @@ -68,6 +70,7 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
> > > >                                       ctx->map_indirections_table, 0);
> > > >        if (ctx->mmap_indirections_table == MAP_FAILED) {
> > > >            trace_ebpf_error("eBPF RSS", "can not mmap eBPF indirection table");
> > > > +        error_setg(errp, "Unable to map eBPF indirection array");
> > > 
> > > Aren't these trace_ebpf_error() calls redundant now?
> > 
> > Yes & no. Errors propagated up the call stack don't get included in
> > any trace output, and so if a caller doesn't log them anywhere they
> > can thus be invisible to someone just looking at trace output.
> > 
> > I could remove them all from the eBPF code though, and put a single
> > trace event in the hw/net/virtio-net.c file instead ? Bit of a bike
> > shed colouring exercise to decide which is best though.
> 
> No problem, I'm fine with this patch.
> 
> Note from experience (although pre-existing in this patch), trace
> events can be very verbose, and a what makes them powerful is we
> can filter particular ones. The following pattern isn't practical
> to filter:
> 
>   trace_foo_error(const char *error_msg);
> 
> While a bit tedious to add, having a single trace event per error
> is way more useful.

Yeah, I agree with you on that. I should look at changing it to add
distinct error probes.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


Re: [PATCH 3/5] ebpf: add formal error reporting to all APIs
Posted by Philippe Mathieu-Daudé 3 months, 2 weeks ago
On 7/8/24 09:37, Philippe Mathieu-Daudé wrote:
> On 6/8/24 17:20, Daniel P. Berrangé wrote:
>> On Tue, Aug 06, 2024 at 05:11:55PM +0200, Philippe Mathieu-Daudé wrote:
>>> On 6/8/24 16:56, Daniel P. Berrangé wrote:
>>>> The eBPF code is currently reporting error messages through trace
>>>> events. Trace events are fine for debugging, but they are not to be
>>>> considered the primary error reporting mechanism, as their output
>>>> is inaccessible to callers.
>>>>
>>>> This adds an "Error **errp" parameter to all methods which have
>>>> important error scenarios to report to the caller.
>>>>
>>>> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>>>> ---
>>>>    ebpf/ebpf_rss.c     | 59 
>>>> ++++++++++++++++++++++++++++++++++++---------
>>>>    ebpf/ebpf_rss.h     | 10 +++++---
>>>>    hw/net/virtio-net.c |  7 +++---
>>>>    3 files changed, 59 insertions(+), 17 deletions(-)
>>>>
>>>> diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c
>>>> index aa7170d997..59854c8b51 100644
>>>> --- a/ebpf/ebpf_rss.c
>>>> +++ b/ebpf/ebpf_rss.c
>>>> @@ -47,13 +47,14 @@ bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx)
>>>>        return ctx != NULL && (ctx->obj != NULL || ctx->program_fd != 
>>>> -1);
>>>>    }
>>>> -static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
>>>> +static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx, Error **errp)
>>>>    {
>>>>        ctx->mmap_configuration = mmap(NULL, qemu_real_host_page_size(),
>>>>                                       PROT_READ | PROT_WRITE, 
>>>> MAP_SHARED,
>>>>                                       ctx->map_configuration, 0);
>>>>        if (ctx->mmap_configuration == MAP_FAILED) {
>>>>            trace_ebpf_error("eBPF RSS", "can not mmap eBPF 
>>>> configuration array");
>>>> +        error_setg(errp, "Unable to map eBPF configuration array");
>>>>            return false;
>>>>        }
>>>>        ctx->mmap_toeplitz_key = mmap(NULL, qemu_real_host_page_size(),
>>>> @@ -61,6 +62,7 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
>>>>                                       ctx->map_toeplitz_key, 0);
>>>>        if (ctx->mmap_toeplitz_key == MAP_FAILED) {
>>>>            trace_ebpf_error("eBPF RSS", "can not mmap eBPF toeplitz 
>>>> key");
>>>> +        error_setg(errp, "Unable to map eBPF toeplitz array");
>>>>            goto toeplitz_fail;
>>>>        }
>>>>        ctx->mmap_indirections_table = mmap(NULL, 
>>>> qemu_real_host_page_size(),
>>>> @@ -68,6 +70,7 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx)
>>>>                                       ctx->map_indirections_table, 0);
>>>>        if (ctx->mmap_indirections_table == MAP_FAILED) {
>>>>            trace_ebpf_error("eBPF RSS", "can not mmap eBPF 
>>>> indirection table");
>>>> +        error_setg(errp, "Unable to map eBPF indirection array");
>>>
>>> Aren't these trace_ebpf_error() calls redundant now?
>>
>> Yes & no. Errors propagated up the call stack don't get included in
>> any trace output, and so if a caller doesn't log them anywhere they
>> can thus be invisible to someone just looking at trace output.
>>
>> I could remove them all from the eBPF code though, and put a single
>> trace event in the hw/net/virtio-net.c file instead ? Bit of a bike
>> shed colouring exercise to decide which is best though.
> 
> No problem, I'm fine with this patch.
> 
> Note from experience (although pre-existing in this patch), trace
> events can be very verbose, and a what makes them powerful is we
> can filter particular ones. The following pattern isn't practical
> to filter:
> 
>    trace_foo_error(const char *error_msg);

I guess I meant:

     trace_foo_error(const char *const error_msg);

(where the string is known at build time).

> While a bit tedious to add, having a single trace event per error
> is way more useful.
> 
> Regards,
> 
> Phil.