diff mbox

[v2,2/3] error: Remove unnecessary local_err variables

Message ID 1465589538-24998-3-git-send-email-ehabkost@redhat.com
State New
Headers show

Commit Message

Eduardo Habkost June 10, 2016, 8:12 p.m. UTC
This patch simplifies code that uses a local_err variable just to
immediately use it for an error_propagate() call.

Coccinelle patch used to perform the changes added to
scripts/coccinelle/remove_local_err.cocci.

Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
---
 block.c                                   |  8 ++------
 block/raw-posix.c                         |  8 ++------
 block/raw_bsd.c                           |  4 +---
 blockdev.c                                | 16 +++++-----------
 hw/s390x/s390-virtio-ccw.c                |  5 +----
 hw/s390x/virtio-ccw.c                     | 28 +++++++---------------------
 scripts/coccinelle/remove_local_err.cocci | 27 +++++++++++++++++++++++++++
 target-i386/cpu.c                         |  4 +---
 8 files changed, 46 insertions(+), 54 deletions(-)
 create mode 100644 scripts/coccinelle/remove_local_err.cocci

Comments

Eric Blake June 10, 2016, 8:59 p.m. UTC | #1
On 06/10/2016 02:12 PM, Eduardo Habkost wrote:
> This patch simplifies code that uses a local_err variable just to
> immediately use it for an error_propagate() call.
> 
> Coccinelle patch used to perform the changes added to
> scripts/coccinelle/remove_local_err.cocci.
> 
> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> ---
>  block.c                                   |  8 ++------
>  block/raw-posix.c                         |  8 ++------
>  block/raw_bsd.c                           |  4 +---
>  blockdev.c                                | 16 +++++-----------
>  hw/s390x/s390-virtio-ccw.c                |  5 +----
>  hw/s390x/virtio-ccw.c                     | 28 +++++++---------------------
>  scripts/coccinelle/remove_local_err.cocci | 27 +++++++++++++++++++++++++++
>  target-i386/cpu.c                         |  4 +---
>  8 files changed, 46 insertions(+), 54 deletions(-)
>  create mode 100644 scripts/coccinelle/remove_local_err.cocci
> 

> +++ b/block.c
> @@ -294,14 +294,12 @@ typedef struct CreateCo {
>  
>  static void coroutine_fn bdrv_create_co_entry(void *opaque)
>  {
> -    Error *local_err = NULL;
>      int ret;
>  
>      CreateCo *cco = opaque;
>      assert(cco->drv);
>  
> -    ret = cco->drv->bdrv_create(cco->filename, cco->opts, &local_err);
> -    error_propagate(&cco->err, local_err);
> +    ret = cco->drv->bdrv_create(cco->filename, cco->opts, &cco->err);
>      cco->ret = ret;

This hunk doesn't get simplified by 3/3; you may want to consider a
manual followup to drop 'int ret' and just assign
cco->drv->bdrv_create() directly to cco->ret.  But doesn't change this
patch.


> +++ b/blockdev.c
> @@ -3654,7 +3654,6 @@ void qmp_blockdev_mirror(const char *device, const char *target,
>      BlockBackend *blk;
>      BlockDriverState *target_bs;
>      AioContext *aio_context;
> -    Error *local_err = NULL;
>  
>      blk = blk_by_name(device);
>      if (!blk) {
> @@ -3678,16 +3677,11 @@ void qmp_blockdev_mirror(const char *device, const char *target,
>  
>      bdrv_set_aio_context(target_bs, aio_context);
>  
> -    blockdev_mirror_common(bs, target_bs,
> -                           has_replaces, replaces, sync,
> -                           has_speed, speed,
> -                           has_granularity, granularity,
> -                           has_buf_size, buf_size,
> -                           has_on_source_error, on_source_error,
> -                           has_on_target_error, on_target_error,
> -                           true, true,
> -                           &local_err);
> -    error_propagate(errp, local_err);
> +    blockdev_mirror_common(bs, target_bs, has_replaces, replaces, sync,
> +                           has_speed, speed, has_granularity, granularity,
> +                           has_buf_size, buf_size, has_on_source_error,
> +                           on_source_error, has_on_target_error,
> +                           on_target_error, true, true, errp);

Coccinelle messes a bit with the formatting (the old way explicitly
tried to pair related has_foo with foo). But I'm going to mess with it
again with my qapi patches for passing a boxed parameter rather than
lots of arguments, so don't worry about it.

> +++ b/scripts/coccinelle/remove_local_err.cocci
> @@ -0,0 +1,27 @@
> +// Replace unnecessary usage of local_err variable with
> +// direct usage of errp argument
> +
> +@@
> +expression list ARGS;
> +expression F2;
> +identifier LOCAL_ERR;
> +expression ERRP;
> +idexpression V;
> +typedef Error;
> +expression I;
> +@@
> + {
> +     ...
> +-    Error *LOCAL_ERR;
> +     ... when != LOCAL_ERR
> +(
> +-    F2(ARGS, &LOCAL_ERR);
> +-    error_propagate(ERRP, LOCAL_ERR);
> ++    F2(ARGS, ERRP);
> +|
> +-    V = F2(ARGS, &LOCAL_ERR);
> +-    error_propagate(ERRP, LOCAL_ERR);
> ++    V = F2(ARGS, ERRP);
> +)
> +     ... when != LOCAL_ERR
> + }

Looks good.
Reviewed-by: Eric Blake <eblake@redhat.com>
Eduardo Habkost June 10, 2016, 10:39 p.m. UTC | #2
On Fri, Jun 10, 2016 at 02:59:55PM -0600, Eric Blake wrote:
> On 06/10/2016 02:12 PM, Eduardo Habkost wrote:
> > This patch simplifies code that uses a local_err variable just to
> > immediately use it for an error_propagate() call.
> > 
> > Coccinelle patch used to perform the changes added to
> > scripts/coccinelle/remove_local_err.cocci.
> > 
> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> > ---
> >  block.c                                   |  8 ++------
> >  block/raw-posix.c                         |  8 ++------
> >  block/raw_bsd.c                           |  4 +---
> >  blockdev.c                                | 16 +++++-----------
> >  hw/s390x/s390-virtio-ccw.c                |  5 +----
> >  hw/s390x/virtio-ccw.c                     | 28 +++++++---------------------
> >  scripts/coccinelle/remove_local_err.cocci | 27 +++++++++++++++++++++++++++
> >  target-i386/cpu.c                         |  4 +---
> >  8 files changed, 46 insertions(+), 54 deletions(-)
> >  create mode 100644 scripts/coccinelle/remove_local_err.cocci
> > 
> 
> > +++ b/block.c
> > @@ -294,14 +294,12 @@ typedef struct CreateCo {
> >  
> >  static void coroutine_fn bdrv_create_co_entry(void *opaque)
> >  {
> > -    Error *local_err = NULL;
> >      int ret;
> >  
> >      CreateCo *cco = opaque;
> >      assert(cco->drv);
> >  
> > -    ret = cco->drv->bdrv_create(cco->filename, cco->opts, &local_err);
> > -    error_propagate(&cco->err, local_err);
> > +    ret = cco->drv->bdrv_create(cco->filename, cco->opts, &cco->err);
> >      cco->ret = ret;
> 
> This hunk doesn't get simplified by 3/3; you may want to consider a
> manual followup to drop 'int ret' and just assign
> cco->drv->bdrv_create() directly to cco->ret.  But doesn't change this
> patch.

This could become yet another Coccinelle script, but we need to
be careful about type conversions, and tell it to do it only if
the types of 'ret', 'cc->drv->bdrv_create()' and 'cco->ret' are
the same.
Cornelia Huck June 13, 2016, 7:44 a.m. UTC | #3
On Fri, 10 Jun 2016 17:12:17 -0300
Eduardo Habkost <ehabkost@redhat.com> wrote:

> This patch simplifies code that uses a local_err variable just to
> immediately use it for an error_propagate() call.
> 
> Coccinelle patch used to perform the changes added to
> scripts/coccinelle/remove_local_err.cocci.
> 
> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> ---
>  block.c                                   |  8 ++------
>  block/raw-posix.c                         |  8 ++------
>  block/raw_bsd.c                           |  4 +---
>  blockdev.c                                | 16 +++++-----------
>  hw/s390x/s390-virtio-ccw.c                |  5 +----
>  hw/s390x/virtio-ccw.c                     | 28 +++++++---------------------

For the two virtio-ccw files:

Acked-by: Cornelia Huck <cornelia.huck@de.ibm.com>

>  scripts/coccinelle/remove_local_err.cocci | 27 +++++++++++++++++++++++++++
>  target-i386/cpu.c                         |  4 +---
>  8 files changed, 46 insertions(+), 54 deletions(-)
>  create mode 100644 scripts/coccinelle/remove_local_err.cocci
Markus Armbruster June 13, 2016, 11:42 a.m. UTC | #4
Eduardo Habkost <ehabkost@redhat.com> writes:

> This patch simplifies code that uses a local_err variable just to
> immediately use it for an error_propagate() call.
>
> Coccinelle patch used to perform the changes added to
> scripts/coccinelle/remove_local_err.cocci.
>
> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
[...]
> diff --git a/scripts/coccinelle/remove_local_err.cocci b/scripts/coccinelle/remove_local_err.cocci
> new file mode 100644
> index 0000000..5f0b6c0
> --- /dev/null
> +++ b/scripts/coccinelle/remove_local_err.cocci
> @@ -0,0 +1,27 @@
> +// Replace unnecessary usage of local_err variable with
> +// direct usage of errp argument
> +
> +@@
> +expression list ARGS;
> +expression F2;
> +identifier LOCAL_ERR;
> +expression ERRP;
> +idexpression V;
> +typedef Error;
> +expression I;

I isn't used.

> +@@
> + {
> +     ...
> +-    Error *LOCAL_ERR;
> +     ... when != LOCAL_ERR
> +(
> +-    F2(ARGS, &LOCAL_ERR);
> +-    error_propagate(ERRP, LOCAL_ERR);
> ++    F2(ARGS, ERRP);
> +|
> +-    V = F2(ARGS, &LOCAL_ERR);
> +-    error_propagate(ERRP, LOCAL_ERR);
> ++    V = F2(ARGS, ERRP);
> +)
> +     ... when != LOCAL_ERR
> + }

There is an (ugly) difference between

    error_setg(&local_err, ...);
    error_propagate(errp, &local_err);

and

    error_setg(errp, ...);

The latter aborts when @errp already contains an error, the former does
nothing.

Your transformation has the error_setg() or similar hidden in F2().  It
can add aborts.

I think it can be salvaged: we know that @errp must not contain an error
on function entry.  If @errp doesn't occur elsewhere in this function,
it cannot pick up an error on the way to the transformed spot.  Can you
add that to your when constraints?

[...]
Eduardo Habkost June 13, 2016, 3:52 p.m. UTC | #5
On Mon, Jun 13, 2016 at 01:42:15PM +0200, Markus Armbruster wrote:
> Eduardo Habkost <ehabkost@redhat.com> writes:
> 
> > This patch simplifies code that uses a local_err variable just to
> > immediately use it for an error_propagate() call.
> >
> > Coccinelle patch used to perform the changes added to
> > scripts/coccinelle/remove_local_err.cocci.
> >
> > Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> [...]
> > diff --git a/scripts/coccinelle/remove_local_err.cocci b/scripts/coccinelle/remove_local_err.cocci
> > new file mode 100644
> > index 0000000..5f0b6c0
> > --- /dev/null
> > +++ b/scripts/coccinelle/remove_local_err.cocci
> > @@ -0,0 +1,27 @@
> > +// Replace unnecessary usage of local_err variable with
> > +// direct usage of errp argument
> > +
> > +@@
> > +expression list ARGS;
> > +expression F2;
> > +identifier LOCAL_ERR;
> > +expression ERRP;
> > +idexpression V;
> > +typedef Error;
> > +expression I;
> 
> I isn't used.
> 
> > +@@
> > + {
> > +     ...
> > +-    Error *LOCAL_ERR;
> > +     ... when != LOCAL_ERR
> > +(
> > +-    F2(ARGS, &LOCAL_ERR);
> > +-    error_propagate(ERRP, LOCAL_ERR);
> > ++    F2(ARGS, ERRP);
> > +|
> > +-    V = F2(ARGS, &LOCAL_ERR);
> > +-    error_propagate(ERRP, LOCAL_ERR);
> > ++    V = F2(ARGS, ERRP);
> > +)
> > +     ... when != LOCAL_ERR
> > + }
> 
> There is an (ugly) difference between
> 
>     error_setg(&local_err, ...);
>     error_propagate(errp, &local_err);
> 
> and
> 
>     error_setg(errp, ...);
> 
> The latter aborts when @errp already contains an error, the former does
> nothing.

Why the difference? Couldn't we change that so that both are equivalent?

> 
> Your transformation has the error_setg() or similar hidden in F2().  It
> can add aborts.
> 
> I think it can be salvaged: we know that @errp must not contain an error
> on function entry.  If @errp doesn't occur elsewhere in this function,
> it cannot pick up an error on the way to the transformed spot.  Can you
> add that to your when constraints?

Do we really know that *errp is NULL on entry? Aren't we allowed to call
functions with a non-NULL *errp?

See, e.g.:

void qmp_guest_suspend_disk(Error **errp)
{
    Error *local_err = NULL;
    GuestSuspendMode *mode = g_new(GuestSuspendMode, 1);

    *mode = GUEST_SUSPEND_MODE_DISK;
    check_suspend_mode(*mode, &local_err);
    acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
    execute_async(do_suspend, mode, &local_err);

    if (local_err) {
        error_propagate(errp, local_err);
        g_free(mode);
    }
}
Eric Blake June 13, 2016, 4:01 p.m. UTC | #6
On 06/13/2016 09:52 AM, Eduardo Habkost wrote:

>>
>> There is an (ugly) difference between
>>
>>     error_setg(&local_err, ...);
>>     error_propagate(errp, &local_err);
>>
>> and
>>
>>     error_setg(errp, ...);
>>
>> The latter aborts when @errp already contains an error, the former does
>> nothing.
> 
> Why the difference? Couldn't we change that so that both are equivalent?

Maybe, but I think it weakens our position. An abort() on an attempt to
incorrectly set an error twice helps catch errors where we are throwing
away a more useful first error message.  The documentation for
error_propagate() already mentioned that it was an exception to the rule.

> 
>>
>> Your transformation has the error_setg() or similar hidden in F2().  It
>> can add aborts.
>>
>> I think it can be salvaged: we know that @errp must not contain an error
>> on function entry.  If @errp doesn't occur elsewhere in this function,
>> it cannot pick up an error on the way to the transformed spot.  Can you
>> add that to your when constraints?
> 
> Do we really know that *errp is NULL on entry? Aren't we allowed to call
> functions with a non-NULL *errp?

Except for error_propagate(), no.

> 
> See, e.g.:
> 
> void qmp_guest_suspend_disk(Error **errp)
> {
>     Error *local_err = NULL;
>     GuestSuspendMode *mode = g_new(GuestSuspendMode, 1);
> 
>     *mode = GUEST_SUSPEND_MODE_DISK;
>     check_suspend_mode(*mode, &local_err);
>     acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
>     execute_async(do_suspend, mode, &local_err);

That usage is a bug.  A Coccinelle script to root out such buggy
instances would be nice.
Markus Armbruster June 13, 2016, 6:49 p.m. UTC | #7
Eric Blake <eblake@redhat.com> writes:

> On 06/13/2016 09:52 AM, Eduardo Habkost wrote:
>
>>>
>>> There is an (ugly) difference between
>>>
>>>     error_setg(&local_err, ...);
>>>     error_propagate(errp, &local_err);
>>>
>>> and
>>>
>>>     error_setg(errp, ...);
>>>
>>> The latter aborts when @errp already contains an error, the former does
>>> nothing.
>> 
>> Why the difference? Couldn't we change that so that both are equivalent?
>
> Maybe, but I think it weakens our position. An abort() on an attempt to
> incorrectly set an error twice helps catch errors where we are throwing
> away a more useful first error message.  The documentation for
> error_propagate() already mentioned that it was an exception to the rule.

For what it's worth, both g_set_error(err, ...) and
g_propagate_error(err, ...) require !err || !*err.  To accumulate
multiple errors so that the first one wins, you have to do something
like

    if (local_err) {
        g_clear_error(errp);
        g_propagate_error(errp, local_err);
    }

error.h happend before we got GLib.  Its designers chose to deviate from
GLib and made error_propagate() accumulate errors.  This trades the
ability to detect misuse for less boilerplate:

    error_propagate(errp, local_err);

Tightening error_propagate() now would lead to a rather tedious hunt for
callers that rely on its accumulating behavior.

We could do it incrementally by renaming error_propagate() to
error_accumulate(), and have a new error_propagate() that's consistent
with error_setg().  Not sure it's worth it.

Loosening error_setg() & friends is also possible, but we'd detect fewer
misuse, and we'd be left with some superfluous code to clean up.  Not
sure that's smart.

>>> Your transformation has the error_setg() or similar hidden in F2().  It
>>> can add aborts.
>>>
>>> I think it can be salvaged: we know that @errp must not contain an error
>>> on function entry.  If @errp doesn't occur elsewhere in this function,
>>> it cannot pick up an error on the way to the transformed spot.  Can you
>>> add that to your when constraints?
>> 
>> Do we really know that *errp is NULL on entry? Aren't we allowed to call
>> functions with a non-NULL *errp?
>
> Except for error_propagate(), no.

By convention, all functions taking an Error **errp argument expect one
that can be safely passed to error_setg().  That means !errp || errp ==
&error_abort || errp == &error_fatal || !*errp.

>> 
>> See, e.g.:
>> 
>> void qmp_guest_suspend_disk(Error **errp)
>> {
>>     Error *local_err = NULL;
>>     GuestSuspendMode *mode = g_new(GuestSuspendMode, 1);
>> 
>>     *mode = GUEST_SUSPEND_MODE_DISK;
>>     check_suspend_mode(*mode, &local_err);
>>     acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
>>     execute_async(do_suspend, mode, &local_err);
>
> That usage is a bug.  A Coccinelle script to root out such buggy
> instances would be nice.

We've discussed this before.  See for instance commit 297a364:

    qapi: Replace uncommon use of the error API by the common one
    
    We commonly use the error API like this:
    
        err = NULL;
        foo(..., &err);
        if (err) {
            goto out;
        }
        bar(..., &err);
    
    Every error source is checked separately.  The second function is only
    called when the first one succeeds.  Both functions are free to pass
    their argument to error_set().  Because error_set() asserts no error
    has been set, this effectively means they must not be called with an
    error set.
    
    The qapi-generated code uses the error API differently:
    
        // *errp was initialized to NULL somewhere up the call chain
        frob(..., errp);
        gnat(..., errp);
    
    Errors accumulate in *errp: first error wins, subsequent errors get
    dropped.  To make this work, the second function does nothing when
    called with an error set.  Requires non-null errp, or else the second
    function can't see the first one fail.
    
    This usage has also bled into visitor tests, and two device model
    object property getters rtc_get_date() and balloon_stats_get_all().
    
    With the "accumulate" technique, you need fewer error checks in
    callers, and buy that with an error check in every callee.  Can be
    nice.
    
    However, mixing the two techniques is confusing.  You can't use the
    "accumulate" technique with functions designed for the "check
    separately" technique.  You can use the "check separately" technique
    with functions designed for the "accumulate" technique, but then
    error_set() can't catch you setting an error more than once.
    
    Standardize on the "check separately" technique for now, because it's
    overwhelmingly prevalent.
Eduardo Habkost June 13, 2016, 7:40 p.m. UTC | #8
On Mon, Jun 13, 2016 at 10:01:16AM -0600, Eric Blake wrote:
> On 06/13/2016 09:52 AM, Eduardo Habkost wrote:
[...]
> > 
> > See, e.g.:
> > 
> > void qmp_guest_suspend_disk(Error **errp)
> > {
> >     Error *local_err = NULL;
> >     GuestSuspendMode *mode = g_new(GuestSuspendMode, 1);
> > 
> >     *mode = GUEST_SUSPEND_MODE_DISK;
> >     check_suspend_mode(*mode, &local_err);
> >     acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
> >     execute_async(do_suspend, mode, &local_err);
> 
> That usage is a bug.  A Coccinelle script to root out such buggy
> instances would be nice.

I've tried to write one, but got lots of false positives due to
error-checking using the function return value, not local_err.
For reference, this is the script I have used:

@@
typedef Error;
identifier F1 !~ "hmp_handle_error|error_free_or_abort";
identifier F2 !~ "hmp_handle_error|error_free_or_abort";
idexpression Error *ERR;
@@
-F1(..., &ERR)
+FIXME_incorrect_error_usage1()
... when != ERR
-F2(..., &ERR)
+FIXME_incorrect_error_usage2()

@@
identifier F1 !~ "hmp_handle_error|error_free_or_abort";
identifier F2 !~ "hmp_handle_error|error_free_or_abort";
idexpression Error **ERRP;
@@
-F1(..., ERRP)
+FIXME_incorrect_error_usage1()
 ... when != ERRP
-F2(..., ERRP)
+FIXME_incorrect_error_usage2()
Eduardo Habkost June 13, 2016, 7:42 p.m. UTC | #9
On Mon, Jun 13, 2016 at 08:49:37PM +0200, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
[...]
> >> 
> >> See, e.g.:
> >> 
> >> void qmp_guest_suspend_disk(Error **errp)
> >> {
> >>     Error *local_err = NULL;
> >>     GuestSuspendMode *mode = g_new(GuestSuspendMode, 1);
> >> 
> >>     *mode = GUEST_SUSPEND_MODE_DISK;
> >>     check_suspend_mode(*mode, &local_err);
> >>     acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
> >>     execute_async(do_suspend, mode, &local_err);
> >
> > That usage is a bug.  A Coccinelle script to root out such buggy
> > instances would be nice.
> 
> We've discussed this before.  See for instance commit 297a364:
> 
>     qapi: Replace uncommon use of the error API by the common one

That explains why I was confused: I remember seeing that QAPI
visitors could be called with *errp set.

I will try to change the script as suggested, to only apply the
changes if errp is never touched before the error_setg() call.
Markus Armbruster June 14, 2016, 8:15 a.m. UTC | #10
Eduardo Habkost <ehabkost@redhat.com> writes:

> On Mon, Jun 13, 2016 at 08:49:37PM +0200, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
> [...]
>> >> 
>> >> See, e.g.:
>> >> 
>> >> void qmp_guest_suspend_disk(Error **errp)
>> >> {
>> >>     Error *local_err = NULL;
>> >>     GuestSuspendMode *mode = g_new(GuestSuspendMode, 1);
>> >> 
>> >>     *mode = GUEST_SUSPEND_MODE_DISK;
>> >>     check_suspend_mode(*mode, &local_err);
>> >>     acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
>> >>     execute_async(do_suspend, mode, &local_err);
>> >
>> > That usage is a bug.  A Coccinelle script to root out such buggy
>> > instances would be nice.
>> 
>> We've discussed this before.  See for instance commit 297a364:
>> 
>>     qapi: Replace uncommon use of the error API by the common one
>
> That explains why I was confused: I remember seeing that QAPI
> visitors could be called with *errp set.

Nothing confuses as effectively as bad examples.

> I will try to change the script as suggested, to only apply the
> changes if errp is never touched before the error_setg() call.

Thanks!
diff mbox

Patch

diff --git a/block.c b/block.c
index ecca55a..d516ab6 100644
--- a/block.c
+++ b/block.c
@@ -294,14 +294,12 @@  typedef struct CreateCo {
 
 static void coroutine_fn bdrv_create_co_entry(void *opaque)
 {
-    Error *local_err = NULL;
     int ret;
 
     CreateCo *cco = opaque;
     assert(cco->drv);
 
-    ret = cco->drv->bdrv_create(cco->filename, cco->opts, &local_err);
-    error_propagate(&cco->err, local_err);
+    ret = cco->drv->bdrv_create(cco->filename, cco->opts, &cco->err);
     cco->ret = ret;
 }
 
@@ -353,7 +351,6 @@  out:
 int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
 {
     BlockDriver *drv;
-    Error *local_err = NULL;
     int ret;
 
     drv = bdrv_find_protocol(filename, true, errp);
@@ -361,8 +358,7 @@  int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
         return -ENOENT;
     }
 
-    ret = bdrv_create(drv, filename, opts, &local_err);
-    error_propagate(errp, local_err);
+    ret = bdrv_create(drv, filename, opts, errp);
     return ret;
 }
 
diff --git a/block/raw-posix.c b/block/raw-posix.c
index cb663d8..d7397bf 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -582,12 +582,10 @@  static int raw_open(BlockDriverState *bs, QDict *options, int flags,
                     Error **errp)
 {
     BDRVRawState *s = bs->opaque;
-    Error *local_err = NULL;
     int ret;
 
     s->type = FTYPE_FILE;
-    ret = raw_open_common(bs, options, flags, 0, &local_err);
-    error_propagate(errp, local_err);
+    ret = raw_open_common(bs, options, flags, 0, errp);
     return ret;
 }
 
@@ -2442,14 +2440,12 @@  static int cdrom_open(BlockDriverState *bs, QDict *options, int flags,
                       Error **errp)
 {
     BDRVRawState *s = bs->opaque;
-    Error *local_err = NULL;
     int ret;
 
     s->type = FTYPE_CD;
 
     /* open will not fail even if no CD is inserted, so add O_NONBLOCK */
-    ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
-    error_propagate(errp, local_err);
+    ret = raw_open_common(bs, options, flags, O_NONBLOCK, errp);
     return ret;
 }
 
diff --git a/block/raw_bsd.c b/block/raw_bsd.c
index 5af11b6..b51ac98 100644
--- a/block/raw_bsd.c
+++ b/block/raw_bsd.c
@@ -190,11 +190,9 @@  static int raw_has_zero_init(BlockDriverState *bs)
 
 static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
 {
-    Error *local_err = NULL;
     int ret;
 
-    ret = bdrv_create_file(filename, opts, &local_err);
-    error_propagate(errp, local_err);
+    ret = bdrv_create_file(filename, opts, errp);
     return ret;
 }
 
diff --git a/blockdev.c b/blockdev.c
index 028dba3..3b6d242 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3654,7 +3654,6 @@  void qmp_blockdev_mirror(const char *device, const char *target,
     BlockBackend *blk;
     BlockDriverState *target_bs;
     AioContext *aio_context;
-    Error *local_err = NULL;
 
     blk = blk_by_name(device);
     if (!blk) {
@@ -3678,16 +3677,11 @@  void qmp_blockdev_mirror(const char *device, const char *target,
 
     bdrv_set_aio_context(target_bs, aio_context);
 
-    blockdev_mirror_common(bs, target_bs,
-                           has_replaces, replaces, sync,
-                           has_speed, speed,
-                           has_granularity, granularity,
-                           has_buf_size, buf_size,
-                           has_on_source_error, on_source_error,
-                           has_on_target_error, on_target_error,
-                           true, true,
-                           &local_err);
-    error_propagate(errp, local_err);
+    blockdev_mirror_common(bs, target_bs, has_replaces, replaces, sync,
+                           has_speed, speed, has_granularity, granularity,
+                           has_buf_size, buf_size, has_on_source_error,
+                           on_source_error, has_on_target_error,
+                           on_target_error, true, true, errp);
 
     aio_context_release(aio_context);
 }
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 95ff5e3..b7112d0 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -180,10 +180,7 @@  static HotplugHandler *s390_get_hotplug_handler(MachineState *machine,
 static void s390_hot_add_cpu(const int64_t id, Error **errp)
 {
     MachineState *machine = MACHINE(qdev_get_machine());
-    Error *err = NULL;
-
-    s390x_new_cpu(machine->cpu_model, id, &err);
-    error_propagate(errp, err);
+    s390x_new_cpu(machine->cpu_model, id, errp);
 }
 
 static void ccw_machine_class_init(ObjectClass *oc, void *data)
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index 464b091..50b0935 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -899,13 +899,11 @@  static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp)
     DeviceState *qdev = DEVICE(ccw_dev);
     VirtIONetCcw *dev = VIRTIO_NET_CCW(ccw_dev);
     DeviceState *vdev = DEVICE(&dev->vdev);
-    Error *err = NULL;
 
     virtio_net_set_netclient_name(&dev->vdev, qdev->id,
                                   object_get_typename(OBJECT(qdev)));
     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
-    object_property_set_bool(OBJECT(vdev), true, "realized", &err);
-    error_propagate(errp, err);
+    object_property_set_bool(OBJECT(vdev), true, "realized", errp);
 }
 
 static void virtio_ccw_net_instance_init(Object *obj)
@@ -922,11 +920,9 @@  static void virtio_ccw_blk_realize(VirtioCcwDevice *ccw_dev, Error **errp)
 {
     VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(ccw_dev);
     DeviceState *vdev = DEVICE(&dev->vdev);
-    Error *err = NULL;
 
     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
-    object_property_set_bool(OBJECT(vdev), true, "realized", &err);
-    error_propagate(errp, err);
+    object_property_set_bool(OBJECT(vdev), true, "realized", errp);
 }
 
 static void virtio_ccw_blk_instance_init(Object *obj)
@@ -946,7 +942,6 @@  static void virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp)
     VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(ccw_dev);
     DeviceState *vdev = DEVICE(&dev->vdev);
     DeviceState *proxy = DEVICE(ccw_dev);
-    Error *err = NULL;
     char *bus_name;
 
     /*
@@ -960,8 +955,7 @@  static void virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp)
     }
 
     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
-    object_property_set_bool(OBJECT(vdev), true, "realized", &err);
-    error_propagate(errp, err);
+    object_property_set_bool(OBJECT(vdev), true, "realized", errp);
 }
 
 
@@ -977,11 +971,9 @@  static void virtio_ccw_balloon_realize(VirtioCcwDevice *ccw_dev, Error **errp)
 {
     VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(ccw_dev);
     DeviceState *vdev = DEVICE(&dev->vdev);
-    Error *err = NULL;
 
     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
-    object_property_set_bool(OBJECT(vdev), true, "realized", &err);
-    error_propagate(errp, err);
+    object_property_set_bool(OBJECT(vdev), true, "realized", errp);
 }
 
 static void virtio_ccw_balloon_instance_init(Object *obj)
@@ -1002,7 +994,6 @@  static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp)
     VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(ccw_dev);
     DeviceState *vdev = DEVICE(&dev->vdev);
     DeviceState *qdev = DEVICE(ccw_dev);
-    Error *err = NULL;
     char *bus_name;
 
     /*
@@ -1016,8 +1007,7 @@  static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp)
     }
 
     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
-    object_property_set_bool(OBJECT(vdev), true, "realized", &err);
-    error_propagate(errp, err);
+    object_property_set_bool(OBJECT(vdev), true, "realized", errp);
 }
 
 static void virtio_ccw_scsi_instance_init(Object *obj)
@@ -1035,11 +1025,9 @@  static void vhost_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp)
 {
     VHostSCSICcw *dev = VHOST_SCSI_CCW(ccw_dev);
     DeviceState *vdev = DEVICE(&dev->vdev);
-    Error *err = NULL;
 
     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
-    object_property_set_bool(OBJECT(vdev), true, "realized", &err);
-    error_propagate(errp, err);
+    object_property_set_bool(OBJECT(vdev), true, "realized", errp);
 }
 
 static void vhost_ccw_scsi_instance_init(Object *obj)
@@ -1856,11 +1844,9 @@  static void virtio_ccw_9p_realize(VirtioCcwDevice *ccw_dev, Error **errp)
 {
     V9fsCCWState *dev = VIRTIO_9P_CCW(ccw_dev);
     DeviceState *vdev = DEVICE(&dev->vdev);
-    Error *err = NULL;
 
     qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
-    object_property_set_bool(OBJECT(vdev), true, "realized", &err);
-    error_propagate(errp, err);
+    object_property_set_bool(OBJECT(vdev), true, "realized", errp);
 }
 
 static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data)
diff --git a/scripts/coccinelle/remove_local_err.cocci b/scripts/coccinelle/remove_local_err.cocci
new file mode 100644
index 0000000..5f0b6c0
--- /dev/null
+++ b/scripts/coccinelle/remove_local_err.cocci
@@ -0,0 +1,27 @@ 
+// Replace unnecessary usage of local_err variable with
+// direct usage of errp argument
+
+@@
+expression list ARGS;
+expression F2;
+identifier LOCAL_ERR;
+expression ERRP;
+idexpression V;
+typedef Error;
+expression I;
+@@
+ {
+     ...
+-    Error *LOCAL_ERR;
+     ... when != LOCAL_ERR
+(
+-    F2(ARGS, &LOCAL_ERR);
+-    error_propagate(ERRP, LOCAL_ERR);
++    F2(ARGS, ERRP);
+|
+-    V = F2(ARGS, &LOCAL_ERR);
+-    error_propagate(ERRP, LOCAL_ERR);
++    V = F2(ARGS, ERRP);
+)
+     ... when != LOCAL_ERR
+ }
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 895a386..2cea40a 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -1820,7 +1820,6 @@  static void x86_cpu_get_feature_words(Object *obj, Visitor *v,
 {
     uint32_t *array = (uint32_t *)opaque;
     FeatureWord w;
-    Error *err = NULL;
     X86CPUFeatureWordInfo word_infos[FEATURE_WORDS] = { };
     X86CPUFeatureWordInfoList list_entries[FEATURE_WORDS] = { };
     X86CPUFeatureWordInfoList *list = NULL;
@@ -1840,8 +1839,7 @@  static void x86_cpu_get_feature_words(Object *obj, Visitor *v,
         list = &list_entries[w];
     }
 
-    visit_type_X86CPUFeatureWordInfoList(v, "feature-words", &list, &err);
-    error_propagate(errp, err);
+    visit_type_X86CPUFeatureWordInfoList(v, "feature-words", &list, errp);
 }
 
 static void x86_get_hv_spinlocks(Object *obj, Visitor *v, const char *name,