[PATCH 1/1] acpica: fix suspend on ThinkPad X1 Carbon 6th

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



[Sent mid-August to the devel@xxxxxxxxxx mailing list and to the three
listed ACPICA maintainers. Didn't see it on the mailing list, and didn't
hear back when I asked mailman@xxxxxxxxxx. Now trying linux-acpi. I'm
watching the list so please don't cc me on replies.]

On a current Lenovo ThinkPad X1 Carbon 6th generation (Kaby Lake), the
BIOS announces only "S0i3" ("Windows Modern Standby") support and not S3
(normal suspend) support:

   https://wiki.archlinux.org/index.php/Lenovo_ThinkPad_X1_Carbon_(Gen_6)
   https://bbs.archlinux.org/viewtopic.php?id=234913
   https://forums.lenovo.com/t5/Linux-Discussion/X1-Carbon-Gen-6-cannot-enter-deep-sleep-S3-state-aka-Suspend-to/td-p/3998182
   https://delta-xi.net/#056

The information from the BIOS is inaccurate: the hardware does in fact
support S3 as hardware sleep state 5. The primary purpose of this patch
is to correct the wrong information from the BIOS on this laptop (as
identified by DMI). The secondary purpose of this patch is to add an
"acpi_force_s3=5" command-line option that lets users manually specify
the same thing on other machines.

Why is this important? Answer: When the kernel doesn't know that the
laptop supports S3, it falls back to using s2idle for suspend. However,
as the above links show, Linux users see much higher power use on this
laptop with s2idle than with S3. Also, for those of us running Linux
inside a VM in Qubes, wakeup from s2idle seems to fail on this laptop.
Presumably the wakeup bug will be found and fixed at some point, and
perhaps s2idle (and/or S0i3) will someday use as little power as S3, but
at least for the moment it's much better to have traditional S3 support.

Why doesn't the BIOS accurately report S3? The Lenovo answer is simply
that this breaks Windows:

   https://forums.lenovo.com/t5/Linux-Discussion/X1-Carbon-Gen-6-cannot-enter-deep-sleep-S3-state-aka-Suspend-to/m-p/3998636/highlight/true#M10573

As the above links also show, Linux users have converged on overriding
the ACPI DSDT for this laptop to enable S3. However, my understanding is
that there's a preference to eliminate ACPI overrides in favor of kernel
patches (for fairly obvious reasons), and that this preference is strong
enough that it doesn't need justification.

I haven't done much Linux kernel programming, so please check carefully
for any coding issues, and please feel free to fix anything that's
wrong. For example, should the DMI information be prioritized over the
command-line option? I don't see a serious argument one way or the
other, and I didn't find any documentation of Linux conventions on this
topic. For the patch the answer is yes, but feel free to fix if it
should be the other way.

What bothers me more is that I haven't found documentation of how this
sort of thing is supposed to be tested. It seems to work on the laptop
where I'm typing this, and some other people have reported success:

   https://marc.info/?l=qubes-users&m=153311827308201
   https://marc.info/?l=qubes-users&m=153356792316425
   https://marc.info/?l=qubes-users&m=153402652424867

But of course what's much more important is whether it accidentally
kicks in on other laptops and non-laptops. For example, what happens if
I screwed up the DMI testing and the code uses hardware sleep state 5 on
other laptops that actually uses a different hardware sleep state? This
would be an obvious thing to test before patch submission, but I didn't
find documentation of any Linux testing mechanism to check this on a
broad range of physical or virtual hardware, or any coding mechanism to
restrict the hardware affected by a patch.

Is this really just something checked by manual code reviews followed
by people screaming if there's a screwup? People with more Linux kernel
experience seem to think that I'm supposed to just go ahead and submit
the patch, so I'm doing that, but I'm still surprised at the apparent
lack of testing infrastructure.

Signed-off-by: "D. J. Bernstein" <djb@xxxxxxxx>


diff -ruw a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c
--- a/drivers/acpi/acpica/hwxface.c	2018-07-31 21:14:22.759000000 +0200
+++ b/drivers/acpi/acpica/hwxface.c	2018-08-01 01:48:01.087000000 +0200
@@ -44,6 +44,7 @@
 #define EXPORT_ACPI_INTERFACES
 
 #include <acpi/acpi.h>
+#include <linux/dmi.h>
 #include "accommon.h"
 #include "acnamesp.h"
 
@@ -440,6 +441,47 @@
 
 ACPI_EXPORT_SYMBOL(acpi_write_bit_register)
 #endif				/* !ACPI_REDUCED_HARDWARE */
+
+/* ----- force_s3 support */
+
+/* Some systems silently support sleep type 5 for S3: */
+/* the hardware supports sleep type 5 but S3 is not in the DSDT. */
+/* Setting force_s3 to 5 will override checking the DSDT. */
+static int force_s3 = -1;
+
+/* Systems known to silently support sleep type 5 for S3. */
+static const struct dmi_system_id force_s3_5[] = {
+	{
+		.ident = "ThinkPad X1 Carbon 6th",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_FAMILY, "ThinkPad X1 Carbon 6th"),
+		},
+	},
+	{ },
+};
+
+int __init acpi_force_s3_init(void)
+{
+	if (dmi_check_system(force_s3_5))
+		force_s3 = 5;
+
+	return 0;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_force_s3_init)
+
+/* Let users handle unknown systems by setting acpi_force_s3=5. */
+static int force_s3_setup(char *str)
+{
+	if (str && *str)
+		if (*str >= '0' && *str <= '7')
+			force_s3 = *str - '0';
+	return 1;
+}
+
+__setup("acpi_force_s3=", force_s3_setup);
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_get_sleep_type_data
@@ -492,6 +534,14 @@
 		return_ACPI_STATUS(AE_BAD_PARAMETER);
 	}
 
+	/* Support force_s3 */
+
+	if (force_s3 >= 0 && sleep_state == ACPI_STATE_S3) {
+		*sleep_type_a = force_s3;
+		*sleep_type_b = force_s3;
+		return_ACPI_STATUS(AE_OK);
+	}
+
 	/* Allocate the evaluation information block */
 
 	info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
diff -ruw a/drivers/acpi/bus.c b/drivers/acpi/bus.c
--- a/drivers/acpi/bus.c	2018-07-31 21:14:22.762000000 +0200
+++ b/drivers/acpi/bus.c	2018-08-01 02:45:53.527000000 +0200
@@ -1192,6 +1192,7 @@
 	printk(KERN_INFO PREFIX "Interpreter enabled\n");
 
 	/* Initialize sleep structures */
+	acpi_force_s3_init();
 	acpi_sleep_init();
 
 	/*
diff -ruw a/drivers/acpi/internal.h b/drivers/acpi/internal.h
--- a/drivers/acpi/internal.h	2018-07-31 21:14:22.762000000 +0200
+++ b/drivers/acpi/internal.h	2018-08-01 01:48:01.088000000 +0200
@@ -183,6 +183,7 @@
 /* External interfaces use first EC only, so remember */
 typedef int (*acpi_ec_query_func) (void *data);
 
+int acpi_force_s3_init(void);
 int acpi_ec_init(void);
 int acpi_ec_ecdt_probe(void);
 int acpi_ec_dsdt_probe(void);

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux