[bpf-next,2/3] libbpf: cleanup after partial failure in bpf_object__pin
diff mbox series

Message ID 20181107224356.73080-3-sdf@google.com
State New
Headers show
Series
  • bpftool: support loading flow dissector
Related show

Commit Message

Stanislav Fomichev Nov. 7, 2018, 10:43 p.m. UTC
bpftool will use bpf_object__pin in the next commit to pin all programs
and maps from the file; in case of a partial failure, we need to get
back to the clean state (undo previous program/map pins).

Signed-off-by: Stanislav Fomichev <sdf@google.com>
---
 tools/lib/bpf/libbpf.c | 58 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 48 insertions(+), 10 deletions(-)

Comments

Jakub Kicinski Nov. 7, 2018, 10:56 p.m. UTC | #1
On Wed,  7 Nov 2018 14:43:55 -0800, Stanislav Fomichev wrote:
> bpftool will use bpf_object__pin in the next commit to pin all programs
> and maps from the file; in case of a partial failure, we need to get
> back to the clean state (undo previous program/map pins).
> 
> Signed-off-by: Stanislav Fomichev <sdf@google.com>
> ---
>  tools/lib/bpf/libbpf.c | 58 ++++++++++++++++++++++++++++++++++--------
>  1 file changed, 48 insertions(+), 10 deletions(-)
> 
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index d6e62e90e8d4..309abe7196f3 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -1803,14 +1803,17 @@ int bpf_object__pin(struct bpf_object *obj, const char *path)
>  
>  		len = snprintf(buf, PATH_MAX, "%s/%s", path,
>  			       bpf_map__name(map));
> -		if (len < 0)
> -			return -EINVAL;
> -		else if (len >= PATH_MAX)
> -			return -ENAMETOOLONG;
> +		if (len < 0) {
> +			err = -EINVAL;
> +			goto err_unpin_maps;
> +		} else if (len >= PATH_MAX) {
> +			err = -ENAMETOOLONG;
> +			goto err_unpin_maps;
> +		}
>  
>  		err = bpf_map__pin(map, buf);
>  		if (err)
> -			return err;
> +			goto err_unpin_maps;
>  	}
>  
>  	bpf_object__for_each_program(prog, obj) {
> @@ -1819,17 +1822,52 @@ int bpf_object__pin(struct bpf_object *obj, const char *path)
>  
>  		len = snprintf(buf, PATH_MAX, "%s/%s", path,
>  			       prog->section_name);
> -		if (len < 0)
> -			return -EINVAL;
> -		else if (len >= PATH_MAX)
> -			return -ENAMETOOLONG;
> +		if (len < 0) {
> +			err = -EINVAL;
> +			goto err_unpin_programs;
> +		} else if (len >= PATH_MAX) {
> +			err = -ENAMETOOLONG;
> +			goto err_unpin_programs;
> +		}
>  
>  		err = bpf_program__pin(prog, buf);
>  		if (err)
> -			return err;
> +			goto err_unpin_programs;
>  	}
>  
>  	return 0;
> +
> +err_unpin_programs:
> +	bpf_object__for_each_program(prog, obj) {
> +		char buf[PATH_MAX];
> +		int len;
> +
> +		len = snprintf(buf, PATH_MAX, "%s/%s", path,
> +			       prog->section_name);
> +		if (len < 0)
> +			continue;
> +		else if (len >= PATH_MAX)
> +			continue;
> +
> +		unlink(buf);

I think that's no bueno, if pin failed because the file already exists
you'll now remove that already existing file.

> +	}
> +
> +err_unpin_maps:
> +	bpf_map__for_each(map, obj) {
> +		char buf[PATH_MAX];
> +		int len;
> +
> +		len = snprintf(buf, PATH_MAX, "%s/%s", path,
> +			       bpf_map__name(map));
> +		if (len < 0)
> +			continue;
> +		else if (len >= PATH_MAX)
> +			continue;
> +
> +		unlink(buf);
> +	}
> +
> +	return err;
>  }
>  
>  void bpf_object__close(struct bpf_object *obj)
Stanislav Fomichev Nov. 7, 2018, 11 p.m. UTC | #2
On 11/07, Jakub Kicinski wrote:
> On Wed,  7 Nov 2018 14:43:55 -0800, Stanislav Fomichev wrote:
> > bpftool will use bpf_object__pin in the next commit to pin all programs
> > and maps from the file; in case of a partial failure, we need to get
> > back to the clean state (undo previous program/map pins).
> > 
> > Signed-off-by: Stanislav Fomichev <sdf@google.com>
> > ---
> >  tools/lib/bpf/libbpf.c | 58 ++++++++++++++++++++++++++++++++++--------
> >  1 file changed, 48 insertions(+), 10 deletions(-)
> > 
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index d6e62e90e8d4..309abe7196f3 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -1803,14 +1803,17 @@ int bpf_object__pin(struct bpf_object *obj, const char *path)
> >  
> >  		len = snprintf(buf, PATH_MAX, "%s/%s", path,
> >  			       bpf_map__name(map));
> > -		if (len < 0)
> > -			return -EINVAL;
> > -		else if (len >= PATH_MAX)
> > -			return -ENAMETOOLONG;
> > +		if (len < 0) {
> > +			err = -EINVAL;
> > +			goto err_unpin_maps;
> > +		} else if (len >= PATH_MAX) {
> > +			err = -ENAMETOOLONG;
> > +			goto err_unpin_maps;
> > +		}
> >  
> >  		err = bpf_map__pin(map, buf);
> >  		if (err)
> > -			return err;
> > +			goto err_unpin_maps;
> >  	}
> >  
> >  	bpf_object__for_each_program(prog, obj) {
> > @@ -1819,17 +1822,52 @@ int bpf_object__pin(struct bpf_object *obj, const char *path)
> >  
> >  		len = snprintf(buf, PATH_MAX, "%s/%s", path,
> >  			       prog->section_name);
> > -		if (len < 0)
> > -			return -EINVAL;
> > -		else if (len >= PATH_MAX)
> > -			return -ENAMETOOLONG;
> > +		if (len < 0) {
> > +			err = -EINVAL;
> > +			goto err_unpin_programs;
> > +		} else if (len >= PATH_MAX) {
> > +			err = -ENAMETOOLONG;
> > +			goto err_unpin_programs;
> > +		}
> >  
> >  		err = bpf_program__pin(prog, buf);
> >  		if (err)
> > -			return err;
> > +			goto err_unpin_programs;
> >  	}
> >  
> >  	return 0;
> > +
> > +err_unpin_programs:
> > +	bpf_object__for_each_program(prog, obj) {
> > +		char buf[PATH_MAX];
> > +		int len;
> > +
> > +		len = snprintf(buf, PATH_MAX, "%s/%s", path,
> > +			       prog->section_name);
> > +		if (len < 0)
> > +			continue;
> > +		else if (len >= PATH_MAX)
> > +			continue;
> > +
> > +		unlink(buf);
> 
> I think that's no bueno, if pin failed because the file already exists
> you'll now remove that already existing file.
How about we check beforehand and bail early if we are going to
overwrite something?

> > +	}
> > +
> > +err_unpin_maps:
> > +	bpf_map__for_each(map, obj) {
> > +		char buf[PATH_MAX];
> > +		int len;
> > +
> > +		len = snprintf(buf, PATH_MAX, "%s/%s", path,
> > +			       bpf_map__name(map));
> > +		if (len < 0)
> > +			continue;
> > +		else if (len >= PATH_MAX)
> > +			continue;
> > +
> > +		unlink(buf);
> > +	}
> > +
> > +	return err;
> >  }
> >  
> >  void bpf_object__close(struct bpf_object *obj)
>
Jakub Kicinski Nov. 7, 2018, 11:06 p.m. UTC | #3
On Wed, 7 Nov 2018 15:00:21 -0800, Stanislav Fomichev wrote:
> > > +err_unpin_programs:
> > > +	bpf_object__for_each_program(prog, obj) {
> > > +		char buf[PATH_MAX];
> > > +		int len;
> > > +
> > > +		len = snprintf(buf, PATH_MAX, "%s/%s", path,
> > > +			       prog->section_name);
> > > +		if (len < 0)
> > > +			continue;
> > > +		else if (len >= PATH_MAX)
> > > +			continue;
> > > +
> > > +		unlink(buf);  
> > 
> > I think that's no bueno, if pin failed because the file already exists
> > you'll now remove that already existing file.  
>
> How about we check beforehand and bail early if we are going to
> overwrite something?

Possible, although the most common way to handle situation like this in
the kernel is to "continue the iteration in reverse" over the list.
I.e. walk the list back.  I think the objects are on a double linked
list.  You may need to add the appropriate foreach macro and helper..
Stanislav Fomichev Nov. 7, 2018, 11:25 p.m. UTC | #4
On 11/07, Jakub Kicinski wrote:
> On Wed, 7 Nov 2018 15:00:21 -0800, Stanislav Fomichev wrote:
> > > > +err_unpin_programs:
> > > > +	bpf_object__for_each_program(prog, obj) {
> > > > +		char buf[PATH_MAX];
> > > > +		int len;
> > > > +
> > > > +		len = snprintf(buf, PATH_MAX, "%s/%s", path,
> > > > +			       prog->section_name);
> > > > +		if (len < 0)
> > > > +			continue;
> > > > +		else if (len >= PATH_MAX)
> > > > +			continue;
> > > > +
> > > > +		unlink(buf);  
> > > 
> > > I think that's no bueno, if pin failed because the file already exists
> > > you'll now remove that already existing file.  
> >
> > How about we check beforehand and bail early if we are going to
> > overwrite something?
> 
> Possible, although the most common way to handle situation like this in
> the kernel is to "continue the iteration in reverse" over the list.
> I.e. walk the list back.  I think the objects are on a double linked
> list.  You may need to add the appropriate foreach macro and helper..
That sounds more complicated than just ensuring that the top directory
for the pins doesn't exist and then rm -rf it on failure.
I'm thinking about copy-pasting rm_rf from perf
(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/perf/util/util.c#n119).
Thoughts?

Btw, current patch won't work because of those /0 added by bpf_program__pin.
Jakub Kicinski Nov. 7, 2018, 11:38 p.m. UTC | #5
On Wed, 7 Nov 2018 15:25:16 -0800, Stanislav Fomichev wrote:
> On 11/07, Jakub Kicinski wrote:
> > On Wed, 7 Nov 2018 15:00:21 -0800, Stanislav Fomichev wrote:  
> > > > > +err_unpin_programs:
> > > > > +	bpf_object__for_each_program(prog, obj) {
> > > > > +		char buf[PATH_MAX];
> > > > > +		int len;
> > > > > +
> > > > > +		len = snprintf(buf, PATH_MAX, "%s/%s", path,
> > > > > +			       prog->section_name);
> > > > > +		if (len < 0)
> > > > > +			continue;
> > > > > +		else if (len >= PATH_MAX)
> > > > > +			continue;
> > > > > +
> > > > > +		unlink(buf);    
> > > > 
> > > > I think that's no bueno, if pin failed because the file already exists
> > > > you'll now remove that already existing file.    
> > >
> > > How about we check beforehand and bail early if we are going to
> > > overwrite something?  
> > 
> > Possible, although the most common way to handle situation like this in
> > the kernel is to "continue the iteration in reverse" over the list.
> > I.e. walk the list back.  I think the objects are on a double linked
> > list.  You may need to add the appropriate foreach macro and helper..  
>
> That sounds more complicated than just ensuring that the top directory
> for the pins doesn't exist and then rm -rf it on failure.

Why would we require that the directory does not exist?  We can
check if it exists and then either create or just pin all in an existing
one.

I don't think it should be that much effort to write a reverse for
loop - it could actually be less LoC than that rm_rf function :)

> I'm thinking about copy-pasting rm_rf from perf
> (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/perf/util/util.c#n119).
> Thoughts?
>
> Btw, current patch won't work because of those /0 added by bpf_program__pin.

Patch
diff mbox series

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index d6e62e90e8d4..309abe7196f3 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1803,14 +1803,17 @@  int bpf_object__pin(struct bpf_object *obj, const char *path)
 
 		len = snprintf(buf, PATH_MAX, "%s/%s", path,
 			       bpf_map__name(map));
-		if (len < 0)
-			return -EINVAL;
-		else if (len >= PATH_MAX)
-			return -ENAMETOOLONG;
+		if (len < 0) {
+			err = -EINVAL;
+			goto err_unpin_maps;
+		} else if (len >= PATH_MAX) {
+			err = -ENAMETOOLONG;
+			goto err_unpin_maps;
+		}
 
 		err = bpf_map__pin(map, buf);
 		if (err)
-			return err;
+			goto err_unpin_maps;
 	}
 
 	bpf_object__for_each_program(prog, obj) {
@@ -1819,17 +1822,52 @@  int bpf_object__pin(struct bpf_object *obj, const char *path)
 
 		len = snprintf(buf, PATH_MAX, "%s/%s", path,
 			       prog->section_name);
-		if (len < 0)
-			return -EINVAL;
-		else if (len >= PATH_MAX)
-			return -ENAMETOOLONG;
+		if (len < 0) {
+			err = -EINVAL;
+			goto err_unpin_programs;
+		} else if (len >= PATH_MAX) {
+			err = -ENAMETOOLONG;
+			goto err_unpin_programs;
+		}
 
 		err = bpf_program__pin(prog, buf);
 		if (err)
-			return err;
+			goto err_unpin_programs;
 	}
 
 	return 0;
+
+err_unpin_programs:
+	bpf_object__for_each_program(prog, obj) {
+		char buf[PATH_MAX];
+		int len;
+
+		len = snprintf(buf, PATH_MAX, "%s/%s", path,
+			       prog->section_name);
+		if (len < 0)
+			continue;
+		else if (len >= PATH_MAX)
+			continue;
+
+		unlink(buf);
+	}
+
+err_unpin_maps:
+	bpf_map__for_each(map, obj) {
+		char buf[PATH_MAX];
+		int len;
+
+		len = snprintf(buf, PATH_MAX, "%s/%s", path,
+			       bpf_map__name(map));
+		if (len < 0)
+			continue;
+		else if (len >= PATH_MAX)
+			continue;
+
+		unlink(buf);
+	}
+
+	return err;
 }
 
 void bpf_object__close(struct bpf_object *obj)