Merge branches 'acpi-apei', 'acpi-wakeup', 'acpi-reboot' and 'acpi-thermal'
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 10 Oct 2022 16:11:11 +0000 (18:11 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 10 Oct 2022 16:11:11 +0000 (18:11 +0200)
Merge additional APEI changes, ACPI updates related to device wakeup and
system restart and ACPI thermal driver cleanups for 6.1-rc1:

 - Fix a memory leak in APEI by avoiding to add do not add task_work to
   kernel threads running when an asynchronous error is detected (Shuai
   Xue).

 - Add ACPI support for handling system wakeups via GPIO wake capable
   IRQs in addition to GPEs (Raul E Rangel).

 - Make the system reboot code put ACPI-enabled systems into the S5
   (system off) state which is necessary for some platforms to work as
   expected (Kai-Heng Feng).

 - Make the white space usage in the ACPI thermal driver more consistent
   and drop redundant code from it (Rafael Wysocki).

* acpi-apei:
  ACPI: APEI: do not add task_work to kernel thread to avoid memory leak

* acpi-wakeup:
  ACPI: PM: Take wake IRQ into consideration when entering suspend-to-idle
  i2c: acpi: Use ACPI wake capability bit to set wake_irq
  ACPI: resources: Add wake_capable parameter to acpi_dev_irq_flags
  gpiolib: acpi: Add wake_capable variants of acpi_dev_gpio_irq_get

* acpi-reboot:
  PM: ACPI: reboot: Reinstate S5 for reboot
  kernel/reboot: Add SYS_OFF_MODE_RESTART_PREPARE mode

* acpi-thermal:
  ACPI: thermal: Drop some redundant code
  ACPI: thermal: Drop redundant parens from expressions
  ACPI: thermal: Use white space more consistently

16 files changed:
drivers/acpi/apei/ghes.c
drivers/acpi/device_pm.c
drivers/acpi/irq.c
drivers/acpi/resource.c
drivers/acpi/sleep.c
drivers/gpio/gpiolib-acpi.c
drivers/gpio/gpiolib-acpi.h
drivers/i2c/i2c-core-acpi.c
drivers/i2c/i2c-core-base.c
drivers/i2c/i2c-core.h
drivers/platform/x86/intel/int3472/tps68470.c
drivers/pnp/pnpacpi/rsparser.c
include/linux/acpi.h
include/linux/ioport.h
include/linux/reboot.h
kernel/reboot.c

index d91ad378c00d604b2c7a897fd2f9769c5e4106f2..80ad530583c9c9c602eb1d17fca4eca7fd74efab 100644 (file)
@@ -985,7 +985,7 @@ static void ghes_proc_in_irq(struct irq_work *irq_work)
                                ghes_estatus_cache_add(generic, estatus);
                }
 
-               if (task_work_pending && current->mm != &init_mm) {
+               if (task_work_pending && current->mm) {
                        estatus_node->task_work.func = ghes_kick_task_work;
                        estatus_node->task_work_cpu = smp_processor_id();
                        ret = task_work_add(current, &estatus_node->task_work,
index d594effe905ffeb9ac5df40fcf83413d1901d4ed..97450f4003cc9befa407496fb13476bc67ea2dc9 100644 (file)
@@ -687,7 +687,22 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev,
                d_min = ret;
                wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid
                        && adev->wakeup.sleep_state >= target_state;
+       } else if (device_may_wakeup(dev) && dev->power.wakeirq) {
+               /*
+                * The ACPI subsystem doesn't manage the wake bit for IRQs
+                * defined with ExclusiveAndWake and SharedAndWake. Instead we
+                * expect them to be managed via the PM subsystem. Drivers
+                * should call dev_pm_set_wake_irq to register an IRQ as a wake
+                * source.
+                *
+                * If a device has a wake IRQ attached we need to check the
+                * _S0W method to get the correct wake D-state. Otherwise we
+                * end up putting the device into D3Cold which will more than
+                * likely disable wake functionality.
+                */
+               wakeup = true;
        } else {
+               /* ACPI GPE is specified in _PRW. */
                wakeup = adev->wakeup.flags.valid;
        }
 
index 4db5bb587599891418799c2a90a0ac90bdb96b3e..1cc4647f78b86d70875f84615b1c131e64d7a4b5 100644 (file)
@@ -147,6 +147,7 @@ struct acpi_irq_parse_one_ctx {
  * @polarity: polarity attributes of hwirq
  * @polarity: polarity attributes of hwirq
  * @shareable: shareable attributes of hwirq
+ * @wake_capable: wake capable attribute of hwirq
  * @ctx: acpi_irq_parse_one_ctx updated by this function
  *
  * Description:
@@ -156,12 +157,13 @@ struct acpi_irq_parse_one_ctx {
 static inline void acpi_irq_parse_one_match(struct fwnode_handle *fwnode,
                                            u32 hwirq, u8 triggering,
                                            u8 polarity, u8 shareable,
+                                           u8 wake_capable,
                                            struct acpi_irq_parse_one_ctx *ctx)
 {
        if (!fwnode)
                return;
        ctx->rc = 0;
-       *ctx->res_flags = acpi_dev_irq_flags(triggering, polarity, shareable);
+       *ctx->res_flags = acpi_dev_irq_flags(triggering, polarity, shareable, wake_capable);
        ctx->fwspec->fwnode = fwnode;
        ctx->fwspec->param[0] = hwirq;
        ctx->fwspec->param[1] = acpi_dev_get_irq_type(triggering, polarity);
@@ -204,7 +206,7 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
                fwnode = acpi_get_gsi_domain_id(irq->interrupts[ctx->index]);
                acpi_irq_parse_one_match(fwnode, irq->interrupts[ctx->index],
                                         irq->triggering, irq->polarity,
-                                        irq->shareable, ctx);
+                                        irq->shareable, irq->wake_capable, ctx);
                return AE_CTRL_TERMINATE;
        case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
                eirq = &ares->data.extended_irq;
@@ -218,7 +220,7 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
                                                      eirq->interrupts[ctx->index]);
                acpi_irq_parse_one_match(fwnode, eirq->interrupts[ctx->index],
                                         eirq->triggering, eirq->polarity,
-                                        eirq->shareable, ctx);
+                                        eirq->shareable, eirq->wake_capable, ctx);
                return AE_CTRL_TERMINATE;
        }
 
index 514d89656dde56cdc543839bb056a1ecccfc852c..6f9489edfb4ee5446d3b2ccc4ac0ba5e5c8f1dbf 100644 (file)
@@ -336,8 +336,9 @@ EXPORT_SYMBOL_GPL(acpi_dev_resource_ext_address_space);
  * @triggering: Triggering type as provided by ACPI.
  * @polarity: Interrupt polarity as provided by ACPI.
  * @shareable: Whether or not the interrupt is shareable.
+ * @wake_capable: Wake capability as provided by ACPI.
  */
-unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable)
+unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable, u8 wake_capable)
 {
        unsigned long flags;
 
@@ -351,6 +352,9 @@ unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable)
        if (shareable == ACPI_SHARED)
                flags |= IORESOURCE_IRQ_SHAREABLE;
 
+       if (wake_capable == ACPI_WAKE_CAPABLE)
+               flags |= IORESOURCE_IRQ_WAKECAPABLE;
+
        return flags | IORESOURCE_IRQ;
 }
 EXPORT_SYMBOL_GPL(acpi_dev_irq_flags);
@@ -468,7 +472,7 @@ static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity,
 
 static void acpi_dev_get_irqresource(struct resource *res, u32 gsi,
                                     u8 triggering, u8 polarity, u8 shareable,
-                                    bool check_override)
+                                    u8 wake_capable, bool check_override)
 {
        int irq, p, t;
 
@@ -501,7 +505,7 @@ static void acpi_dev_get_irqresource(struct resource *res, u32 gsi,
                }
        }
 
-       res->flags = acpi_dev_irq_flags(triggering, polarity, shareable);
+       res->flags = acpi_dev_irq_flags(triggering, polarity, shareable, wake_capable);
        irq = acpi_register_gsi(NULL, gsi, triggering, polarity);
        if (irq >= 0) {
                res->start = irq;
@@ -549,7 +553,8 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
                }
                acpi_dev_get_irqresource(res, irq->interrupts[index],
                                         irq->triggering, irq->polarity,
-                                        irq->shareable, true);
+                                        irq->shareable, irq->wake_capable,
+                                        true);
                break;
        case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
                ext_irq = &ares->data.extended_irq;
@@ -560,7 +565,8 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
                if (is_gsi(ext_irq))
                        acpi_dev_get_irqresource(res, ext_irq->interrupts[index],
                                         ext_irq->triggering, ext_irq->polarity,
-                                        ext_irq->shareable, false);
+                                        ext_irq->shareable, ext_irq->wake_capable,
+                                        false);
                else
                        irqresource_disabled(res, 0);
                break;
index ad4b2987b3d6ec0bc71f4d918992576ae28edce4..0b557c0d405ef1945024374556ddaf7e831ea4a4 100644 (file)
@@ -1088,6 +1088,14 @@ int __init acpi_sleep_init(void)
                register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
                                         SYS_OFF_PRIO_FIRMWARE,
                                         acpi_power_off, NULL);
+
+               /*
+                * Windows uses S5 for reboot, so some BIOSes depend on it to
+                * perform proper reboot.
+                */
+               register_sys_off_handler(SYS_OFF_MODE_RESTART_PREPARE,
+                                        SYS_OFF_PRIO_FIRMWARE,
+                                        acpi_power_off_prepare, NULL);
        } else {
                acpi_no_s5 = true;
        }
index 9be1376f9a627f493f61351edf44a00fd334e3a1..1f2ade475b36cb2e92eb2a2d7ea354fe57b059b8 100644 (file)
@@ -741,6 +741,7 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
                lookup->info.pin_config = agpio->pin_config;
                lookup->info.debounce = agpio->debounce_timeout;
                lookup->info.gpioint = gpioint;
+               lookup->info.wake_capable = agpio->wake_capable == ACPI_WAKE_CAPABLE;
 
                /*
                 * Polarity and triggering are only specified for GpioInt
@@ -987,10 +988,11 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
 }
 
 /**
- * acpi_dev_gpio_irq_get_by() - Find GpioInt and translate it to Linux IRQ number
+ * acpi_dev_gpio_irq_wake_get_by() - Find GpioInt and translate it to Linux IRQ number
  * @adev: pointer to a ACPI device to get IRQ from
  * @name: optional name of GpioInt resource
  * @index: index of GpioInt resource (starting from %0)
+ * @wake_capable: Set to true if the IRQ is wake capable
  *
  * If the device has one or more GpioInt resources, this function can be
  * used to translate from the GPIO offset in the resource to the Linux IRQ
@@ -1002,9 +1004,13 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
  * The function takes optional @name parameter. If the resource has a property
  * name, then only those will be taken into account.
  *
+ * The GPIO is considered wake capable if the GpioInt resource specifies
+ * SharedAndWake or ExclusiveAndWake.
+ *
  * Return: Linux IRQ number (> %0) on success, negative errno on failure.
  */
-int acpi_dev_gpio_irq_get_by(struct acpi_device *adev, const char *name, int index)
+int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *name, int index,
+                                 bool *wake_capable)
 {
        int idx, i;
        unsigned int irq_flags;
@@ -1061,13 +1067,16 @@ int acpi_dev_gpio_irq_get_by(struct acpi_device *adev, const char *name, int ind
                                dev_dbg(&adev->dev, "IRQ %d already in use\n", irq);
                        }
 
+                       if (wake_capable)
+                               *wake_capable = info.wake_capable;
+
                        return irq;
                }
 
        }
        return -ENOENT;
 }
-EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_get_by);
+EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_wake_get_by);
 
 static acpi_status
 acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
index e476558d947136d12d62abd29c26ca6b9b2d741b..1ac6816839dbce7544843dcaa2b47e5826f041c8 100644 (file)
@@ -18,6 +18,7 @@ struct acpi_device;
  * @pin_config: pin bias as provided by ACPI
  * @polarity: interrupt polarity as provided by ACPI
  * @triggering: triggering type as provided by ACPI
+ * @wake_capable: wake capability as provided by ACPI
  * @debounce: debounce timeout as provided by ACPI
  * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping
  */
@@ -28,6 +29,7 @@ struct acpi_gpio_info {
        int pin_config;
        int polarity;
        int triggering;
+       bool wake_capable;
        unsigned int debounce;
        unsigned int quirks;
 };
index 08b561f0709d5d8f48bdc14da706942ee199f914..f88386d732d2b39a51e357c66ba16d8df8b5638c 100644 (file)
@@ -137,6 +137,11 @@ static const struct acpi_device_id i2c_acpi_ignored_device_ids[] = {
        {}
 };
 
+struct i2c_acpi_irq_context {
+       int irq;
+       bool wake_capable;
+};
+
 static int i2c_acpi_do_lookup(struct acpi_device *adev,
                              struct i2c_acpi_lookup *lookup)
 {
@@ -168,13 +173,19 @@ static int i2c_acpi_do_lookup(struct acpi_device *adev,
        return 0;
 }
 
-static int i2c_acpi_add_resource(struct acpi_resource *ares, void *data)
+static int i2c_acpi_add_irq_resource(struct acpi_resource *ares, void *data)
 {
-       int *irq = data;
+       struct i2c_acpi_irq_context *irq_ctx = data;
        struct resource r;
 
-       if (*irq <= 0 && acpi_dev_resource_interrupt(ares, 0, &r))
-               *irq = i2c_dev_irq_from_resources(&r, 1);
+       if (irq_ctx->irq > 0)
+               return 1;
+
+       if (!acpi_dev_resource_interrupt(ares, 0, &r))
+               return 1;
+
+       irq_ctx->irq = i2c_dev_irq_from_resources(&r, 1);
+       irq_ctx->wake_capable = r.flags & IORESOURCE_IRQ_WAKECAPABLE;
 
        return 1; /* No need to add resource to the list */
 }
@@ -182,31 +193,40 @@ static int i2c_acpi_add_resource(struct acpi_resource *ares, void *data)
 /**
  * i2c_acpi_get_irq - get device IRQ number from ACPI
  * @client: Pointer to the I2C client device
+ * @wake_capable: Set to true if the IRQ is wake capable
  *
  * Find the IRQ number used by a specific client device.
  *
  * Return: The IRQ number or an error code.
  */
-int i2c_acpi_get_irq(struct i2c_client *client)
+int i2c_acpi_get_irq(struct i2c_client *client, bool *wake_capable)
 {
        struct acpi_device *adev = ACPI_COMPANION(&client->dev);
        struct list_head resource_list;
-       int irq = -ENOENT;
+       struct i2c_acpi_irq_context irq_ctx = {
+               .irq = -ENOENT,
+       };
        int ret;
 
        INIT_LIST_HEAD(&resource_list);
 
        ret = acpi_dev_get_resources(adev, &resource_list,
-                                    i2c_acpi_add_resource, &irq);
+                                    i2c_acpi_add_irq_resource, &irq_ctx);
        if (ret < 0)
                return ret;
 
        acpi_dev_free_resource_list(&resource_list);
 
-       if (irq == -ENOENT)
-               irq = acpi_dev_gpio_irq_get(adev, 0);
+       if (irq_ctx.irq == -ENOENT)
+               irq_ctx.irq = acpi_dev_gpio_irq_wake_get(adev, 0, &irq_ctx.wake_capable);
+
+       if (irq_ctx.irq < 0)
+               return irq_ctx.irq;
+
+       if (wake_capable)
+               *wake_capable = irq_ctx.wake_capable;
 
-       return irq;
+       return irq_ctx.irq;
 }
 
 static int i2c_acpi_get_info(struct acpi_device *adev,
index 91007558bcb26012716bc0bbe95f57097cb4fc5d..fc4b85fb90b1b7bff38f576c293c87dc60fa8fa2 100644 (file)
@@ -487,7 +487,11 @@ static int i2c_device_probe(struct device *dev)
                        if (irq == -EINVAL || irq == -ENODATA)
                                irq = of_irq_get(dev->of_node, 0);
                } else if (ACPI_COMPANION(dev)) {
-                       irq = i2c_acpi_get_irq(client);
+                       bool wake_capable;
+
+                       irq = i2c_acpi_get_irq(client, &wake_capable);
+                       if (irq > 0 && wake_capable)
+                               client->flags |= I2C_CLIENT_WAKE;
                }
                if (irq == -EPROBE_DEFER) {
                        status = irq;
index 87e2c914f1c57ba8a6d98ceb1a22331cbea8e4c0..1247e6e6e97517afdd045c939cb7eee9bd0fd2bc 100644 (file)
@@ -61,11 +61,11 @@ static inline int __i2c_check_suspended(struct i2c_adapter *adap)
 #ifdef CONFIG_ACPI
 void i2c_acpi_register_devices(struct i2c_adapter *adap);
 
-int i2c_acpi_get_irq(struct i2c_client *client);
+int i2c_acpi_get_irq(struct i2c_client *client, bool *wake_capable);
 #else /* CONFIG_ACPI */
 static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { }
 
-static inline int i2c_acpi_get_irq(struct i2c_client *client)
+static inline int i2c_acpi_get_irq(struct i2c_client *client, bool *wake_capable)
 {
        return 0;
 }
index 49fc379fe680abbac986f384759bb0004610fd9b..62093fde187f82c173080f296286c2ee6ea822fe 100644 (file)
@@ -128,15 +128,15 @@ skl_int3472_fill_clk_pdata(struct device *dev, struct tps68470_clk_platform_data
        for_each_acpi_consumer_dev(adev, consumer) {
                sensor_name = devm_kasprintf(dev, GFP_KERNEL, I2C_DEV_NAME_FORMAT,
                                             acpi_dev_name(consumer));
-               if (!sensor_name)
+               if (!sensor_name) {
+                       acpi_dev_put(consumer);
                        return -ENOMEM;
+               }
 
                (*clk_pdata)->consumers[i].consumer_dev_name = sensor_name;
                i++;
        }
 
-       acpi_dev_put(consumer);
-
        return n_consumers;
 }
 
index da78dc77aed32e4fbd589d138f13fa3cdcbba4d8..4f05f610391b006c9415c0e2537b5e3ae9260c6c 100644 (file)
@@ -206,7 +206,8 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
                if (i >= 0) {
                        flags = acpi_dev_irq_flags(gpio->triggering,
                                                   gpio->polarity,
-                                                  gpio->shareable);
+                                                  gpio->shareable,
+                                                  gpio->wake_capable);
                } else {
                        flags = IORESOURCE_DISABLED;
                }
@@ -315,7 +316,7 @@ static __init void pnpacpi_parse_irq_option(struct pnp_dev *dev,
                if (p->interrupts[i])
                        __set_bit(p->interrupts[i], map.bits);
 
-       flags = acpi_dev_irq_flags(p->triggering, p->polarity, p->shareable);
+       flags = acpi_dev_irq_flags(p->triggering, p->polarity, p->shareable, p->wake_capable);
        pnp_register_irq_resource(dev, option_flags, &map, flags);
 }
 
@@ -339,7 +340,7 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev,
                }
        }
 
-       flags = acpi_dev_irq_flags(p->triggering, p->polarity, p->shareable);
+       flags = acpi_dev_irq_flags(p->triggering, p->polarity, p->shareable, p->wake_capable);
        pnp_register_irq_resource(dev, option_flags, &map, flags);
 }
 
index 2f9193b8dfc1b4d2f13c78078de2b9d2a320fae8..83a4ffbbbfd6f8c340d10b85183088db7c61a1b8 100644 (file)
@@ -498,7 +498,7 @@ bool acpi_dev_resource_address_space(struct acpi_resource *ares,
                                     struct resource_win *win);
 bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares,
                                         struct resource_win *win);
-unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable);
+unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable, u8 wake_capable);
 unsigned int acpi_dev_get_irq_type(int triggering, int polarity);
 bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
                                 struct resource *res);
@@ -1210,7 +1210,8 @@ bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
                                struct acpi_resource_gpio **agpio);
 bool acpi_gpio_get_io_resource(struct acpi_resource *ares,
                               struct acpi_resource_gpio **agpio);
-int acpi_dev_gpio_irq_get_by(struct acpi_device *adev, const char *name, int index);
+int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *name, int index,
+                                 bool *wake_capable);
 #else
 static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
                                              struct acpi_resource_gpio **agpio)
@@ -1222,16 +1223,28 @@ static inline bool acpi_gpio_get_io_resource(struct acpi_resource *ares,
 {
        return false;
 }
-static inline int acpi_dev_gpio_irq_get_by(struct acpi_device *adev,
-                                          const char *name, int index)
+static inline int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *name,
+                                               int index, bool *wake_capable)
 {
        return -ENXIO;
 }
 #endif
 
+static inline int acpi_dev_gpio_irq_wake_get(struct acpi_device *adev, int index,
+                                            bool *wake_capable)
+{
+       return acpi_dev_gpio_irq_wake_get_by(adev, NULL, index, wake_capable);
+}
+
+static inline int acpi_dev_gpio_irq_get_by(struct acpi_device *adev, const char *name,
+                                          int index)
+{
+       return acpi_dev_gpio_irq_wake_get_by(adev, name, index, NULL);
+}
+
 static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
 {
-       return acpi_dev_gpio_irq_get_by(adev, NULL, index);
+       return acpi_dev_gpio_irq_wake_get_by(adev, NULL, index, NULL);
 }
 
 /* Device properties */
index 616b683563a97046cb862ec11238572783579702..3baeea4d903bfd1212083fce0d11913931b1e30a 100644 (file)
@@ -79,7 +79,8 @@ struct resource {
 #define IORESOURCE_IRQ_HIGHLEVEL       (1<<2)
 #define IORESOURCE_IRQ_LOWLEVEL                (1<<3)
 #define IORESOURCE_IRQ_SHAREABLE       (1<<4)
-#define IORESOURCE_IRQ_OPTIONAL        (1<<5)
+#define IORESOURCE_IRQ_OPTIONAL                (1<<5)
+#define IORESOURCE_IRQ_WAKECAPABLE     (1<<6)
 
 /* PnP DMA specific bits (IORESOURCE_BITS) */
 #define IORESOURCE_DMA_TYPE_MASK       (3<<0)
index e5d9ef886179c0924a12f98ca8e378a1462e3606..2b6bb593be5b655b3251506841b227813525454c 100644 (file)
@@ -105,6 +105,14 @@ enum sys_off_mode {
         */
        SYS_OFF_MODE_POWER_OFF,
 
+       /**
+        * @SYS_OFF_MODE_RESTART_PREPARE:
+        *
+        * Handlers prepare system to be restarted. Handlers are
+        * allowed to sleep.
+        */
+       SYS_OFF_MODE_RESTART_PREPARE,
+
        /**
         * @SYS_OFF_MODE_RESTART:
         *
index 3c35445bf5ad34cad207b91e8086e8d8498fa6f6..3bba88c7ffc6be6b86ee69d1b73f64d7beb70542 100644 (file)
@@ -243,6 +243,17 @@ void migrate_to_reboot_cpu(void)
        set_cpus_allowed_ptr(current, cpumask_of(cpu));
 }
 
+/*
+ *     Notifier list for kernel code which wants to be called
+ *     to prepare system for restart.
+ */
+static BLOCKING_NOTIFIER_HEAD(restart_prep_handler_list);
+
+static void do_kernel_restart_prepare(void)
+{
+       blocking_notifier_call_chain(&restart_prep_handler_list, 0, NULL);
+}
+
 /**
  *     kernel_restart - reboot the system
  *     @cmd: pointer to buffer containing command to execute for restart
@@ -254,6 +265,7 @@ void migrate_to_reboot_cpu(void)
 void kernel_restart(char *cmd)
 {
        kernel_restart_prepare(cmd);
+       do_kernel_restart_prepare();
        migrate_to_reboot_cpu();
        syscore_shutdown();
        if (!cmd)
@@ -396,6 +408,11 @@ register_sys_off_handler(enum sys_off_mode mode,
                handler->list = &power_off_handler_list;
                break;
 
+       case SYS_OFF_MODE_RESTART_PREPARE:
+               handler->list = &restart_prep_handler_list;
+               handler->blocking = true;
+               break;
+
        case SYS_OFF_MODE_RESTART:
                handler->list = &restart_handler_list;
                break;