diff mbox series

[v2] firmware: arm_sdei: Make sdei_unregister_ghes() return void

Message ID 20221220154447.12341-1-u.kleine-koenig@pengutronix.de
State New
Headers show
Series [v2] firmware: arm_sdei: Make sdei_unregister_ghes() return void | expand

Commit Message

Uwe Kleine-König Dec. 20, 2022, 3:44 p.m. UTC
Unregistering a ghes shouldn't fail (because how can firmware refuse to
disable an event on unregister). And the callers are not really in a
position to handle errors. (Note: The return value of platform remove
callbacks is ignored.) So make sdei_unregister_ghes() return void and
add warnings at the few locations that can theoretically fail.

!IS_ENABLED(CONFIG_ACPI_APEI_GHES) and
!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE) cannot be hit here, because if
these aren't given, ghex_probe() already fails and so ghes_remove()
isn't called.

This change improves the behaviour in the error case. Without it the
platform code emits a very generic (and so very unhelpful) error
message. Now the warning is at least helpful.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
Hello,

Changes since (implicit) v1: Add the if (...) BUG() part to fix a linker
error with CONFIG_ARM_SDE_INTERFACE disabled.

Best regards
Uwe

 drivers/acpi/apei/ghes.c    | 19 ++++++++++++-------
 drivers/firmware/arm_sdei.c | 14 +++++++-------
 include/linux/arm_sdei.h    |  2 +-
 3 files changed, 20 insertions(+), 15 deletions(-)

Comments

Rafael J. Wysocki Dec. 21, 2022, 1:53 p.m. UTC | #1
On Tue, Dec 20, 2022 at 4:45 PM Uwe Kleine-König
<u.kleine-koenig@pengutronix.de> wrote:
>
> Unregistering a ghes shouldn't fail (because how can firmware refuse to
> disable an event on unregister). And the callers are not really in a
> position to handle errors. (Note: The return value of platform remove
> callbacks is ignored.) So make sdei_unregister_ghes() return void and
> add warnings at the few locations that can theoretically fail.
>
> !IS_ENABLED(CONFIG_ACPI_APEI_GHES) and
> !IS_ENABLED(CONFIG_ARM_SDE_INTERFACE) cannot be hit here, because if
> these aren't given, ghex_probe() already fails and so ghes_remove()
> isn't called.
>
> This change improves the behaviour in the error case. Without it the
> platform code emits a very generic (and so very unhelpful) error
> message. Now the warning is at least helpful.
>
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> ---
> Hello,
>
> Changes since (implicit) v1: Add the if (...) BUG() part to fix a linker
> error with CONFIG_ARM_SDE_INTERFACE disabled.
>
> Best regards
> Uwe
>
>  drivers/acpi/apei/ghes.c    | 19 ++++++++++++-------
>  drivers/firmware/arm_sdei.c | 14 +++++++-------
>  include/linux/arm_sdei.h    |  2 +-
>  3 files changed, 20 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
> index 066dc1f5c235..7d705930e21b 100644
> --- a/drivers/acpi/apei/ghes.c
> +++ b/drivers/acpi/apei/ghes.c
> @@ -1275,12 +1275,20 @@ static int apei_sdei_register_ghes(struct ghes *ghes)
>                                  ghes_sdei_critical_callback);
>  }
>
> -static int apei_sdei_unregister_ghes(struct ghes *ghes)
> +static void apei_sdei_unregister_ghes(struct ghes *ghes)
>  {
> +       /*
> +        * If CONFIG_ARM_SDE_INTERFACE isn't enabled apei_sdei_register_ghes()
> +        * cannot have been called successfully. So ghes_remove() won't be
> +        * called because either ghes_probe() failed or the notify type isn't
> +        * ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED.
> +        * Note the if statement below is necessary to prevent a linker error as
> +        * the compiler has no chance to understand the above correlation.
> +        */
>         if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
> -               return -EOPNOTSUPP;
> +               BUG();

Well, you could just provide an empty stub for the !CONFIG_ARM_SDE_INTERFACE.

It would be cleaner and probably fewer lines of code too.

>
> -       return sdei_unregister_ghes(ghes);
> +       sdei_unregister_ghes(ghes);
>  }
>
>  static int ghes_probe(struct platform_device *ghes_dev)
> @@ -1421,7 +1429,6 @@ static int ghes_probe(struct platform_device *ghes_dev)
>
>  static int ghes_remove(struct platform_device *ghes_dev)
>  {
> -       int rc;
>         struct ghes *ghes;
>         struct acpi_hest_generic *generic;
>
> @@ -1455,9 +1462,7 @@ static int ghes_remove(struct platform_device *ghes_dev)
>                 ghes_nmi_remove(ghes);
>                 break;
>         case ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED:
> -               rc = apei_sdei_unregister_ghes(ghes);
> -               if (rc)
> -                       return rc;
> +               apei_sdei_unregister_ghes(ghes);
>                 break;
>         default:
>                 BUG();
> diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
> index 1e1a51510e83..7af619464183 100644
> --- a/drivers/firmware/arm_sdei.c
> +++ b/drivers/firmware/arm_sdei.c
> @@ -889,7 +889,7 @@ int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb,
>         return err;
>  }
>
> -int sdei_unregister_ghes(struct ghes *ghes)
> +void sdei_unregister_ghes(struct ghes *ghes)
>  {
>         int i;
>         int err;
> @@ -897,16 +897,15 @@ int sdei_unregister_ghes(struct ghes *ghes)
>
>         might_sleep();
>
> -       if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES))
> -               return -EOPNOTSUPP;
> -
>         /*
>          * The event may be running on another CPU. Disable it
>          * to stop new events, then try to unregister a few times.
>          */
>         err = sdei_event_disable(event_num);
> -       if (err)
> -               return err;
> +       if (err) {
> +               dev_warn(ghes->dev, "Failed to disable event %u: %pe\n", event_num, ERR_PTR(err));
> +               return;
> +       }
>
>         for (i = 0; i < 3; i++) {
>                 err = sdei_event_unregister(event_num);
> @@ -916,7 +915,8 @@ int sdei_unregister_ghes(struct ghes *ghes)
>                 schedule();
>         }
>
> -       return err;
> +       if (err)
> +               dev_warn(ghes->dev, "Failed to disable event %u: %pe\n", event_num, ERR_PTR(err));
>  }
>
>  static int sdei_get_conduit(struct platform_device *pdev)
> diff --git a/include/linux/arm_sdei.h b/include/linux/arm_sdei.h
> index 14dc461b0e82..0812af4530a4 100644
> --- a/include/linux/arm_sdei.h
> +++ b/include/linux/arm_sdei.h
> @@ -40,7 +40,7 @@ int sdei_event_disable(u32 event_num);
>  /* GHES register/unregister helpers */
>  int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb,
>                        sdei_event_callback *critical_cb);
> -int sdei_unregister_ghes(struct ghes *ghes);
> +void sdei_unregister_ghes(struct ghes *ghes);
>
>  #ifdef CONFIG_ARM_SDE_INTERFACE
>  /* For use by arch code when CPU hotplug notifiers are not appropriate. */
> --
> 2.38.1
>
Uwe Kleine-König Dec. 21, 2022, 6:21 p.m. UTC | #2
Hello Rafael,

On Wed, Dec 21, 2022 at 02:53:05PM +0100, Rafael J. Wysocki wrote:
> On Tue, Dec 20, 2022 at 4:45 PM Uwe Kleine-König
> <u.kleine-koenig@pengutronix.de> wrote:
> > [...]
> > diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
> > index 066dc1f5c235..7d705930e21b 100644
> > --- a/drivers/acpi/apei/ghes.c
> > +++ b/drivers/acpi/apei/ghes.c
> > @@ -1275,12 +1275,20 @@ static int apei_sdei_register_ghes(struct ghes *ghes)
> >                                  ghes_sdei_critical_callback);
> >  }
> >
> > -static int apei_sdei_unregister_ghes(struct ghes *ghes)
> > +static void apei_sdei_unregister_ghes(struct ghes *ghes)
> >  {
> > +       /*
> > +        * If CONFIG_ARM_SDE_INTERFACE isn't enabled apei_sdei_register_ghes()
> > +        * cannot have been called successfully. So ghes_remove() won't be
> > +        * called because either ghes_probe() failed or the notify type isn't
> > +        * ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED.
> > +        * Note the if statement below is necessary to prevent a linker error as
> > +        * the compiler has no chance to understand the above correlation.
> > +        */
> >         if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
> > -               return -EOPNOTSUPP;
> > +               BUG();
> 
> Well, you could just provide an empty stub for the !CONFIG_ARM_SDE_INTERFACE.
> 
> It would be cleaner and probably fewer lines of code too.

It's you who cares for this code, but I'd prefer my option. If we assume
the describing comment would have a similar length, we're saving 3 or
four lines of code here but need 3 lines for the #if / #else / #endif
plus the stub definition. And compared to my suggested solution we don't
catch someone introducing a (bogus) call to apei_sdei_unregister_ghes()
(or sdei_unregister_ghes()). And (again IMHO) two different
implementations are harder to grasp than a single with an if.

If you don't like the BUG, a plain return is in my eyes the next best
option which is semantically equivalent to an empty stub.

If you still like the stub better (or a return instead of the BUG), I
can send a v3, just tell me your preference.

Best regards
Uwe
Uwe Kleine-König April 12, 2023, 2:55 p.m. UTC | #3
Hello Rafael,

On Wed, Dec 21, 2022 at 07:21:38PM +0100, Uwe Kleine-König wrote:
> On Wed, Dec 21, 2022 at 02:53:05PM +0100, Rafael J. Wysocki wrote:
> > On Tue, Dec 20, 2022 at 4:45 PM Uwe Kleine-König
> > <u.kleine-koenig@pengutronix.de> wrote:
> > > [...]
> > > diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
> > > index 066dc1f5c235..7d705930e21b 100644
> > > --- a/drivers/acpi/apei/ghes.c
> > > +++ b/drivers/acpi/apei/ghes.c
> > > @@ -1275,12 +1275,20 @@ static int apei_sdei_register_ghes(struct ghes *ghes)
> > >                                  ghes_sdei_critical_callback);
> > >  }
> > >
> > > -static int apei_sdei_unregister_ghes(struct ghes *ghes)
> > > +static void apei_sdei_unregister_ghes(struct ghes *ghes)
> > >  {
> > > +       /*
> > > +        * If CONFIG_ARM_SDE_INTERFACE isn't enabled apei_sdei_register_ghes()
> > > +        * cannot have been called successfully. So ghes_remove() won't be
> > > +        * called because either ghes_probe() failed or the notify type isn't
> > > +        * ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED.
> > > +        * Note the if statement below is necessary to prevent a linker error as
> > > +        * the compiler has no chance to understand the above correlation.
> > > +        */
> > >         if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
> > > -               return -EOPNOTSUPP;
> > > +               BUG();
> > 
> > Well, you could just provide an empty stub for the !CONFIG_ARM_SDE_INTERFACE.
> > 
> > It would be cleaner and probably fewer lines of code too.
> 
> It's you who cares for this code, but I'd prefer my option. If we assume
> the describing comment would have a similar length, we're saving 3 or
> four lines of code here but need 3 lines for the #if / #else / #endif
> plus the stub definition. And compared to my suggested solution we don't
> catch someone introducing a (bogus) call to apei_sdei_unregister_ghes()
> (or sdei_unregister_ghes()). And (again IMHO) two different
> implementations are harder to grasp than a single with an if.
> 
> If you don't like the BUG, a plain return is in my eyes the next best
> option which is semantically equivalent to an empty stub.
> 
> If you still like the stub better (or a return instead of the BUG), I
> can send a v3, just tell me your preference.

I work on changes that depend on a solution here. However you didn't
tell me your preference here. I'm unsure if this means that this
discussion fell through the cracks, or if it annoys you and you still
prefer the cpp #ifdef solution. A note from your side would be very
welcome.

Best regards
Uwe
Rafael J. Wysocki April 12, 2023, 6:33 p.m. UTC | #4
On Wed, Apr 12, 2023 at 4:55 PM Uwe Kleine-König
<u.kleine-koenig@pengutronix.de> wrote:
>
> Hello Rafael,
>
> On Wed, Dec 21, 2022 at 07:21:38PM +0100, Uwe Kleine-König wrote:
> > On Wed, Dec 21, 2022 at 02:53:05PM +0100, Rafael J. Wysocki wrote:
> > > On Tue, Dec 20, 2022 at 4:45 PM Uwe Kleine-König
> > > <u.kleine-koenig@pengutronix.de> wrote:
> > > > [...]
> > > > diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
> > > > index 066dc1f5c235..7d705930e21b 100644
> > > > --- a/drivers/acpi/apei/ghes.c
> > > > +++ b/drivers/acpi/apei/ghes.c
> > > > @@ -1275,12 +1275,20 @@ static int apei_sdei_register_ghes(struct ghes *ghes)
> > > >                                  ghes_sdei_critical_callback);
> > > >  }
> > > >
> > > > -static int apei_sdei_unregister_ghes(struct ghes *ghes)
> > > > +static void apei_sdei_unregister_ghes(struct ghes *ghes)
> > > >  {
> > > > +       /*
> > > > +        * If CONFIG_ARM_SDE_INTERFACE isn't enabled apei_sdei_register_ghes()
> > > > +        * cannot have been called successfully. So ghes_remove() won't be
> > > > +        * called because either ghes_probe() failed or the notify type isn't
> > > > +        * ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED.
> > > > +        * Note the if statement below is necessary to prevent a linker error as
> > > > +        * the compiler has no chance to understand the above correlation.
> > > > +        */
> > > >         if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
> > > > -               return -EOPNOTSUPP;
> > > > +               BUG();
> > >
> > > Well, you could just provide an empty stub for the !CONFIG_ARM_SDE_INTERFACE.
> > >
> > > It would be cleaner and probably fewer lines of code too.
> >
> > It's you who cares for this code, but I'd prefer my option. If we assume
> > the describing comment would have a similar length, we're saving 3 or
> > four lines of code here but need 3 lines for the #if / #else / #endif
> > plus the stub definition. And compared to my suggested solution we don't
> > catch someone introducing a (bogus) call to apei_sdei_unregister_ghes()
> > (or sdei_unregister_ghes()). And (again IMHO) two different
> > implementations are harder to grasp than a single with an if.
> >
> > If you don't like the BUG, a plain return is in my eyes the next best
> > option which is semantically equivalent to an empty stub.
> >
> > If you still like the stub better (or a return instead of the BUG), I
> > can send a v3, just tell me your preference.
>
> I work on changes that depend on a solution here. However you didn't
> tell me your preference here. I'm unsure if this means that this
> discussion fell through the cracks, or if it annoys you and you still
> prefer the cpp #ifdef solution. A note from your side would be very
> welcome.

Well, every time I see BUG() in the code I wonder if crashing the
kernel really is the best thing one can do should the execution reach
that point.

In any case, it's not my opinion that matters the most regarding
APEI/GHES, so I would like Tony/Boris/James to speak up here.
Uwe Kleine-König May 30, 2023, 7:47 a.m. UTC | #5
Hello,

On Wed, Apr 12, 2023 at 08:33:15PM +0200, Rafael J. Wysocki wrote:
> On Wed, Apr 12, 2023 at 4:55 PM Uwe Kleine-König
> <u.kleine-koenig@pengutronix.de> wrote:
> > On Wed, Dec 21, 2022 at 07:21:38PM +0100, Uwe Kleine-König wrote:
> > > On Wed, Dec 21, 2022 at 02:53:05PM +0100, Rafael J. Wysocki wrote:
> > > > On Tue, Dec 20, 2022 at 4:45 PM Uwe Kleine-König
> > > > <u.kleine-koenig@pengutronix.de> wrote:
> > > > > [...]
> > > > > diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
> > > > > index 066dc1f5c235..7d705930e21b 100644
> > > > > --- a/drivers/acpi/apei/ghes.c
> > > > > +++ b/drivers/acpi/apei/ghes.c
> > > > > @@ -1275,12 +1275,20 @@ static int apei_sdei_register_ghes(struct ghes *ghes)
> > > > >                                  ghes_sdei_critical_callback);
> > > > >  }
> > > > >
> > > > > -static int apei_sdei_unregister_ghes(struct ghes *ghes)
> > > > > +static void apei_sdei_unregister_ghes(struct ghes *ghes)
> > > > >  {
> > > > > +       /*
> > > > > +        * If CONFIG_ARM_SDE_INTERFACE isn't enabled apei_sdei_register_ghes()
> > > > > +        * cannot have been called successfully. So ghes_remove() won't be
> > > > > +        * called because either ghes_probe() failed or the notify type isn't
> > > > > +        * ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED.
> > > > > +        * Note the if statement below is necessary to prevent a linker error as
> > > > > +        * the compiler has no chance to understand the above correlation.
> > > > > +        */
> > > > >         if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
> > > > > -               return -EOPNOTSUPP;
> > > > > +               BUG();
> > > >
> > > > Well, you could just provide an empty stub for the !CONFIG_ARM_SDE_INTERFACE.
> > > >
> > > > It would be cleaner and probably fewer lines of code too.
> > >
> > > It's you who cares for this code, but I'd prefer my option. If we assume
> > > the describing comment would have a similar length, we're saving 3 or
> > > four lines of code here but need 3 lines for the #if / #else / #endif
> > > plus the stub definition. And compared to my suggested solution we don't
> > > catch someone introducing a (bogus) call to apei_sdei_unregister_ghes()
> > > (or sdei_unregister_ghes()). And (again IMHO) two different
> > > implementations are harder to grasp than a single with an if.
> > >
> > > If you don't like the BUG, a plain return is in my eyes the next best
> > > option which is semantically equivalent to an empty stub.
> > >
> > > If you still like the stub better (or a return instead of the BUG), I
> > > can send a v3, just tell me your preference.
> >
> > I work on changes that depend on a solution here. However you didn't
> > tell me your preference here. I'm unsure if this means that this
> > discussion fell through the cracks, or if it annoys you and you still
> > prefer the cpp #ifdef solution. A note from your side would be very
> > welcome.
> 
> Well, every time I see BUG() in the code I wonder if crashing the
> kernel really is the best thing one can do should the execution reach
> that point.
> 
> In any case, it's not my opinion that matters the most regarding
> APEI/GHES, so I would like Tony/Boris/James to speak up here.

hmm, they didn't speak up so this patch is stalled. I added them to
"To:" (instead of Cc: before) in this mail, let's see if that helps.

Can you please state your preferred solution that I can properly prepare
this driver for the conversion of the remove callback?!

Best regards
Uwe
diff mbox series

Patch

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 066dc1f5c235..7d705930e21b 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -1275,12 +1275,20 @@  static int apei_sdei_register_ghes(struct ghes *ghes)
 				 ghes_sdei_critical_callback);
 }
 
-static int apei_sdei_unregister_ghes(struct ghes *ghes)
+static void apei_sdei_unregister_ghes(struct ghes *ghes)
 {
+	/*
+	 * If CONFIG_ARM_SDE_INTERFACE isn't enabled apei_sdei_register_ghes()
+	 * cannot have been called successfully. So ghes_remove() won't be
+	 * called because either ghes_probe() failed or the notify type isn't
+	 * ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED.
+	 * Note the if statement below is necessary to prevent a linker error as
+	 * the compiler has no chance to understand the above correlation.
+	 */
 	if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
-		return -EOPNOTSUPP;
+		BUG();
 
-	return sdei_unregister_ghes(ghes);
+	sdei_unregister_ghes(ghes);
 }
 
 static int ghes_probe(struct platform_device *ghes_dev)
@@ -1421,7 +1429,6 @@  static int ghes_probe(struct platform_device *ghes_dev)
 
 static int ghes_remove(struct platform_device *ghes_dev)
 {
-	int rc;
 	struct ghes *ghes;
 	struct acpi_hest_generic *generic;
 
@@ -1455,9 +1462,7 @@  static int ghes_remove(struct platform_device *ghes_dev)
 		ghes_nmi_remove(ghes);
 		break;
 	case ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED:
-		rc = apei_sdei_unregister_ghes(ghes);
-		if (rc)
-			return rc;
+		apei_sdei_unregister_ghes(ghes);
 		break;
 	default:
 		BUG();
diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index 1e1a51510e83..7af619464183 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -889,7 +889,7 @@  int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb,
 	return err;
 }
 
-int sdei_unregister_ghes(struct ghes *ghes)
+void sdei_unregister_ghes(struct ghes *ghes)
 {
 	int i;
 	int err;
@@ -897,16 +897,15 @@  int sdei_unregister_ghes(struct ghes *ghes)
 
 	might_sleep();
 
-	if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES))
-		return -EOPNOTSUPP;
-
 	/*
 	 * The event may be running on another CPU. Disable it
 	 * to stop new events, then try to unregister a few times.
 	 */
 	err = sdei_event_disable(event_num);
-	if (err)
-		return err;
+	if (err) {
+		dev_warn(ghes->dev, "Failed to disable event %u: %pe\n", event_num, ERR_PTR(err));
+		return;
+	}
 
 	for (i = 0; i < 3; i++) {
 		err = sdei_event_unregister(event_num);
@@ -916,7 +915,8 @@  int sdei_unregister_ghes(struct ghes *ghes)
 		schedule();
 	}
 
-	return err;
+	if (err)
+		dev_warn(ghes->dev, "Failed to disable event %u: %pe\n", event_num, ERR_PTR(err));
 }
 
 static int sdei_get_conduit(struct platform_device *pdev)
diff --git a/include/linux/arm_sdei.h b/include/linux/arm_sdei.h
index 14dc461b0e82..0812af4530a4 100644
--- a/include/linux/arm_sdei.h
+++ b/include/linux/arm_sdei.h
@@ -40,7 +40,7 @@  int sdei_event_disable(u32 event_num);
 /* GHES register/unregister helpers */
 int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb,
 		       sdei_event_callback *critical_cb);
-int sdei_unregister_ghes(struct ghes *ghes);
+void sdei_unregister_ghes(struct ghes *ghes);
 
 #ifdef CONFIG_ARM_SDE_INTERFACE
 /* For use by arch code when CPU hotplug notifiers are not appropriate. */