On Tue, May 29 2018, Dan Carpenter wrote: > On Mon, May 21, 2018 at 09:37:31AM +1000, NeilBrown wrote: >> On Fri, May 18 2018, Kamal Heib wrote: >> >> > Simplify the code of allocate and cleanup RX ring resources by using >> > helper functions, also make sure to free the allocated resources in >> > cause of allocation failure. >> > >> > Signed-off-by: Kamal Heib <[email protected]> >> > --- >> > drivers/staging/mt7621-eth/mtk_eth_soc.c | 122 >> > ++++++++++++++++++++----------- >> > 1 file changed, 81 insertions(+), 41 deletions(-) >> > >> .... >> > static int mtk_dma_rx_alloc(struct mtk_eth *eth, struct mtk_rx_ring *ring) >> > { >> > - int i, pad = 0; >> > + int err; >> > >> > ring->frag_size = mtk_max_frag_size(ETH_DATA_LEN); >> > ring->rx_buf_size = mtk_max_buf_size(ring->frag_size); >> > @@ -317,38 +366,23 @@ static int mtk_dma_rx_alloc(struct mtk_eth *eth, >> > struct mtk_rx_ring *ring) >> > ring->rx_data = kcalloc(ring->rx_ring_size, sizeof(*ring->rx_data), >> > GFP_KERNEL); >> > if (!ring->rx_data) >> > - goto no_rx_mem; >> > + return -ENOMEM; >> >> Hi >> I haven't tested this patch yet (will try to in the next day or so) but >> it mostly looks good to me. >> I would rather see the above "return -ENOMEM" as a "goto free_rx_data". >> Having a single error-exit is generally more maintainable than having >> lots of separate 'return's, and kfree() is quite happy to be asked to >> kfree(NULL). >> In fact, all of the free function (even dma_free_coherent) cope >> ok will not having anything to free. As devm_kzallo() and kcalloc() >> are used, all the pointers default to NULL which is safe. I would would >> prefer a single 'nomem' label which did: >> >> nomem: >> dma_free_coherent(eth->dev, ring->rx_ring_size * sizeof(*ring->rx_dma), >> ring->rx_dma, ring->rx_phys); >> mtk_rx_free_frags(ring); >> kfree(ring->rx_data); >> return -ENOMEM; >> >> >> But that is just my personal preference. > > You're sort of advocating two closely related styles of error handling. > The first is a do-nothing error handling instead of direct returns. The > other style is a do-everything style error handling.
Yes, exactly. And when there is nothing that needs doing, "everything"
and "nothing" are the same thing.
>
> Direct returns are preferable to do-nothing gotos because they are more
> readable. "return -ENOMEM;" is more clear than "ret = -ENOMEM;
> goto free_rx_data;". The second one seems clear but it doesn't free
> anything so the label name is actively misleading and confusing. Do
> nothing gotos introduce "forgot to set the error code" bugs.
I don't like direct returns because they do not make modification easy.
If I find a need to allocate something else earlier, I need to go
and find all the direct returns and turn them into gotos. I prefer to
make them gotos from the start.
And your examples are not good. My preferred pattern is:
ret = -ENOMEM;
foo = alloc_foo();
if (!foo)
goto abort;
You may only need to set ret once or twice.
I see nothing misleading or confusing there.
>
> The do-everything style error handling is the most bug prone style in
> the kernel. So far this week I have seen 4 buggy one label patches vs
> 1 buggy multi label patch.
I cannot challenge your statistics, but I wonder if you might be
throwing the baby out with the bath water here. Undoubtedly it is
possible to write do-everything error handling badly. It is also
possible to write it well. I'm not convinced it is fair blame the bad
code on the broad description "do everything error handling".
>
> I feel slightly bad picking on you but with your sample code which uses
> the nomem: label, you can't free dma resources which haven't been
> allocated. I know you would have spotted it if you were writing a real
> patch instead of just throwing together sample code in your email
> client. But the second issue is that you can't call
> mtk_rx_free_frags(ring); when "ring->rx_data" is NULL or it will Oops.
> That's subtle and so far as I know, static analysis would not warn about
> it.
"obviously" we would first fix mtk_rx_free_frags() to do the right thing
if ring->rx_data were NULL. This is a crucial part of robust error
handling.
The very best error handling style is devm_*. When that is not
practical, kfree() style, which quietly accepts NULL, is a good second
best.
Could it be that the various bugs you have seen could be blamed on
resource-release function which do not handle NULL well? When these
bugs were fixed, were they fixed by fixing the resource-release
function (which would benefit many) or were they fixed by making the
code more robust against a poor interface by adding more checking?
>
> Multi label error handling is self documenting so you don't need to jump
> to the bottom of the function. You only need to track the most recently
> allocated resource.
>
> foo = allocate_foo();
> if (!foo)
> return -ENOMEM;
>
> bar = allocate_bar();
> if (!bar) {
> ret = -ENOMEM;
> goto err_free_foo;
> }
I despise this structure because I cannot simply add
baz = allocate_baz();
if (!baz) {
ret = -ENOMEM;
goto err_???;
}
in the between these two. I would need to also change the err_free_foo
to err_free_baz.
Possibly if the convention was "goto err_baz_failed" it might be
workable. Then the failure code would get
free_baz(baz);
err_baz_failed:
added to it. That would be less despicable, but not as good have
ensuring that every free_XXX() correctly handled NULL.
>
> This style of error handling is the most maintainable. For example, I
> see bugs where people change a function from returning NULL to returning
> an error pointer but the error handling was relying on that kfree(NULL)
> is a no-op. If you only free resources which have been allocated then
> you avoid that.
You've just made an excellent case for changing kfree() to something like
if (unlikely(ZERO_OR_NULL_PTR(objp) || IS_ERR(objp)))
return;
I look forward do seeing your patch :-)
>
> One argument I have heard is that using single exit style code will
> prevent locking bugs where people do a direct return without dropping
> the lock. I once looked at git history to find if this was true or not.
> My conclusion was that there are some people who add direct returns
> without thinking about locking at all. Which ever style of error
> handling you use will not affect them.
>
> Sorry for the rant... I should probably try to put together some
> figures to back this up... But I think that if you look at the number
> of bugs, multi label error handling is going to be the clear winner.
No need to apologize, I enjoyed your rant - it gave me an excuse to rant
in return ;-)
Maybe more use of IS_ERR_OR_NULL() in resource release functions would
tip the scales concerning which pattern is the clear winner.
Thanks,
NeilBrown
>
> regards,
> dan carpenter
signature.asc
Description: PGP signature
_______________________________________________ devel mailing list [email protected] http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
