--- /dev/null
+From stable-bounces@linux.kernel.org Sun Feb 3 22:29:29 2008
+From: Len Brown <lenb@kernel.org>
+Date: Mon, 4 Feb 2008 00:38:13 -0500
+Subject: ACPI: sync blacklist w/ latest
+To: stable@kernel.org
+Cc: linux-acpi@vger.kernel.org, linux-kernel@vger.kernel.org
+Message-ID: <200802040038.14079.lenb@kernel.org>
+Content-Disposition: inline
+
+
+From: Len Brown <len.brown@intel.com>
+
+This patch is appropriate for supporting a 2.6.23-based products.
+
+Signed-off-by: Len Brown <len.brown@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/acpi/blacklist.c | 388 +++++++++++++++++++++++++++++++++++++++++++++-
+ drivers/acpi/osl.c | 173 +++++++++++++++-----
+ drivers/firmware/dmi-id.c | 2
+ include/linux/acpi.h | 7
+ include/linux/dmi.h | 2
+ 5 files changed, 523 insertions(+), 49 deletions(-)
+
+--- a/drivers/acpi/blacklist.c
++++ b/drivers/acpi/blacklist.c
+@@ -3,6 +3,7 @@
+ *
+ * Check to see if the given machine has a known bad ACPI BIOS
+ * or if the BIOS is too old.
++ * Check given machine against acpi_osi_dmi_table[].
+ *
+ * Copyright (C) 2004 Len Brown <len.brown@intel.com>
+ * Copyright (C) 2002 Andy Grover <andrew.grover@intel.com>
+@@ -50,6 +51,8 @@ struct acpi_blacklist_item {
+ u32 is_critical_error;
+ };
+
++static struct dmi_system_id acpi_osi_dmi_table[] __initdata;
++
+ /*
+ * POLICY: If *anything* doesn't work, put it on the blacklist.
+ * If they are critical errors, mark it critical, and abort driver load.
+@@ -67,8 +70,6 @@ static struct acpi_blacklist_item acpi_b
+ /* IBM 600E - _ADR should return 7, but it returns 1 */
+ {"IBM ", "TP600E ", 0x00000105, ACPI_SIG_DSDT, less_than_or_equal,
+ "Incorrect _ADR", 1},
+- {"ASUS\0\0", "P2B-S ", 0, ACPI_SIG_DSDT, all_versions,
+- "Bogus PCI routing", 1},
+
+ {""}
+ };
+@@ -165,5 +166,388 @@ int __init acpi_blacklisted(void)
+
+ blacklisted += blacklist_by_year();
+
++ dmi_check_system(acpi_osi_dmi_table);
++
+ return blacklisted;
+ }
++#ifdef CONFIG_DMI
++static int __init dmi_enable_osi_linux(struct dmi_system_id *d)
++{
++ acpi_dmi_osi_linux(1, d); /* enable */
++ return 0;
++}
++static int __init dmi_disable_osi_linux(struct dmi_system_id *d)
++{
++ acpi_dmi_osi_linux(0, d); /* disable */
++ return 0;
++}
++static int __init dmi_unknown_osi_linux(struct dmi_system_id *d)
++{
++ acpi_dmi_osi_linux(-1, d); /* unknown */
++ return 0;
++}
++
++/*
++ * Most BIOS that invoke OSI(Linux) do nothing with it.
++ * But some cause Linux to break.
++ * Only a couple use it to make Linux run better.
++ *
++ * Thus, Linux should continue to disable OSI(Linux) by default,
++ * should continue to discourage BIOS writers from using it, and
++ * should whitelist the few existing systems that require it.
++ *
++ * If it appears clear a vendor isn't using OSI(Linux)
++ * for anything constructive, blacklist them by name to disable
++ * unnecessary dmesg warnings on all of their products.
++ */
++
++static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
++ /*
++ * Disable OSI(Linux) warnings on all "Acer, inc."
++ *
++ * _OSI(Linux) disables the latest Windows BIOS code:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5050"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5580"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 3010"),
++ * _OSI(Linux) effect unknown:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Ferrari 5000"),
++ */
++ /*
++ * note that dmi_check_system() uses strstr()
++ * to match sub-strings rather than !strcmp(),
++ * so "Acer" below matches "Acer, inc." above.
++ */
++ /*
++ * Disable OSI(Linux) warnings on all "Acer"
++ *
++ * _OSI(Linux) effect unknown:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720Z"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5520"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 6460"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 7510"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Extensa 5220"),
++ */
++ {
++ .callback = dmi_unknown_osi_linux,
++ .ident = "Acer",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ },
++ },
++ /*
++ * Disable OSI(Linux) warnings on all "Apple Computer, Inc."
++ *
++ * _OSI(Linux) confirmed to be a NOP:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "MacBook1,1"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "MacBook2,1"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,2"),
++ * _OSI(Linux) effect unknown:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "MacPro2,1"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro1,1"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"),
++ */
++ {
++ .callback = dmi_disable_osi_linux,
++ .ident = "Apple",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."),
++ },
++ },
++ /*
++ * Disable OSI(Linux) warnings on all "BenQ"
++ *
++ * _OSI(Linux) confirmed to be a NOP:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Joybook S31"),
++ */
++ {
++ .callback = dmi_disable_osi_linux,
++ .ident = "BenQ",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "BenQ"),
++ },
++ },
++ /*
++ * Disable OSI(Linux) warnings on all "Clevo Co."
++ *
++ * _OSI(Linux) confirmed to be a NOP:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "M570RU"),
++ */
++ {
++ .callback = dmi_disable_osi_linux,
++ .ident = "Clevo",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Clevo Co."),
++ },
++ },
++ /*
++ * Disable OSI(Linux) warnings on all "COMPAL"
++ *
++ * _OSI(Linux) confirmed to be a NOP:
++ * DMI_MATCH(DMI_BOARD_NAME, "HEL8X"),
++ * _OSI(Linux) unknown effect:
++ * DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
++ */
++ {
++ .callback = dmi_unknown_osi_linux,
++ .ident = "Compal",
++ .matches = {
++ DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"),
++ },
++ },
++ { /* OSI(Linux) touches USB, unknown side-effect */
++ .callback = dmi_disable_osi_linux,
++ .ident = "Dell Dimension 5150",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM051"),
++ },
++ },
++ { /* OSI(Linux) is a NOP */
++ .callback = dmi_disable_osi_linux,
++ .ident = "Dell",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1501"),
++ },
++ },
++ { /* OSI(Linux) effect unknown */
++ .callback = dmi_unknown_osi_linux,
++ .ident = "Dell",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D830"),
++ },
++ },
++ { /* OSI(Linux) effect unknown */
++ .callback = dmi_unknown_osi_linux,
++ .ident = "Dell",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX620"),
++ },
++ },
++ { /* OSI(Linux) effect unknown */
++ .callback = dmi_unknown_osi_linux,
++ .ident = "Dell",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1900"),
++ },
++ },
++ { /* OSI(Linux) touches USB */
++ .callback = dmi_disable_osi_linux,
++ .ident = "Dell",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation 390"),
++ },
++ },
++ { /* OSI(Linux) is a NOP */
++ .callback = dmi_disable_osi_linux,
++ .ident = "Dell Vostro 1000",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1000"),
++ },
++ },
++ { /* OSI(Linux) effect unknown */
++ .callback = dmi_unknown_osi_linux,
++ .ident = "Dell",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge SC440"),
++ },
++ },
++ { /* OSI(Linux) effect unknown */
++ .callback = dmi_unknown_osi_linux,
++ .ident = "Dialogue Flybook V5",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Dialogue Technology Corporation"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Flybook V5"),
++ },
++ },
++ /*
++ * Disable OSI(Linux) warnings on all "FUJITSU SIEMENS"
++ *
++ * _OSI(Linux) disables latest Windows BIOS code:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2510"),
++ * _OSI(Linux) confirmed to be a NOP:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 1536"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 1556"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 1546"),
++ * _OSI(Linux) unknown effect:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Amilo M1425"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Amilo Si 1520"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"),
++ */
++ {
++ .callback = dmi_disable_osi_linux,
++ .ident = "Fujitsu Siemens",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
++ },
++ },
++ /*
++ * Disable OSI(Linux) warnings on all "Hewlett-Packard"
++ *
++ * _OSI(Linux) confirmed to be a NOP:
++ * .ident = "HP Pavilion tx 1000"
++ * DMI_MATCH(DMI_BOARD_NAME, "30BF"),
++ * .ident = "HP Pavilion dv2000"
++ * DMI_MATCH(DMI_BOARD_NAME, "30B5"),
++ * .ident = "HP Pavilion dv5000",
++ * DMI_MATCH(DMI_BOARD_NAME, "30A7"),
++ * .ident = "HP Pavilion dv6300 30BC",
++ * DMI_MATCH(DMI_BOARD_NAME, "30BC"),
++ * .ident = "HP Pavilion dv6000",
++ * DMI_MATCH(DMI_BOARD_NAME, "30B7"),
++ * DMI_MATCH(DMI_BOARD_NAME, "30B8"),
++ * .ident = "HP Pavilion dv9000",
++ * DMI_MATCH(DMI_BOARD_NAME, "30B9"),
++ * .ident = "HP Pavilion dv9500",
++ * DMI_MATCH(DMI_BOARD_NAME, "30CB"),
++ * .ident = "HP/Compaq Presario C500",
++ * DMI_MATCH(DMI_BOARD_NAME, "30C6"),
++ * .ident = "HP/Compaq Presario F500",
++ * DMI_MATCH(DMI_BOARD_NAME, "30D3"),
++ * _OSI(Linux) unknown effect:
++ * .ident = "HP Pavilion dv6500",
++ * DMI_MATCH(DMI_BOARD_NAME, "30D0"),
++ */
++ {
++ .callback = dmi_disable_osi_linux,
++ .ident = "Hewlett-Packard",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
++ },
++ },
++ /*
++ * Lenovo has a mix of systems OSI(Linux) situations
++ * and thus we can not wildcard the vendor.
++ *
++ * _OSI(Linux) helps sound
++ * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"),
++ * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"),
++ * _OSI(Linux) is a NOP:
++ * DMI_MATCH(DMI_PRODUCT_VERSION, "3000 N100"),
++ */
++ {
++ .callback = dmi_enable_osi_linux,
++ .ident = "Lenovo ThinkPad R61",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"),
++ },
++ },
++ {
++ .callback = dmi_enable_osi_linux,
++ .ident = "Lenovo ThinkPad T61",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"),
++ },
++ },
++ {
++ .callback = dmi_unknown_osi_linux,
++ .ident = "Lenovo 3000 V100",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "LENOVO3000 V100"),
++ },
++ },
++ {
++ .callback = dmi_disable_osi_linux,
++ .ident = "Lenovo 3000 N100",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "3000 N100"),
++ },
++ },
++ /*
++ * Disable OSI(Linux) warnings on all "LG Electronics"
++ *
++ * _OSI(Linux) confirmed to be a NOP:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "P1-J150B"),
++ * with DMI_MATCH(DMI_BOARD_NAME, "ROCKY"),
++ *
++ * unknown:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "S1-MDGDG"),
++ * with DMI_MATCH(DMI_BOARD_NAME, "ROCKY"),
++ */
++ {
++ .callback = dmi_disable_osi_linux,
++ .ident = "LG",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
++ },
++ },
++ /* NEC - OSI(Linux) effect unknown */
++ {
++ .callback = dmi_unknown_osi_linux,
++ .ident = "NEC VERSA M360",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "NEC Computers SAS"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "NEC VERSA M360"),
++ },
++ },
++ /*
++ * Disable OSI(Linux) warnings on all "Samsung Electronics"
++ *
++ * OSI(Linux) disables PNP0C32 and other BIOS code for Windows:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "R40P/R41P"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "R59P/R60P/R61P"),
++ */
++ {
++ .callback = dmi_disable_osi_linux,
++ .ident = "Samsung",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
++ },
++ },
++ /*
++ * Disable OSI(Linux) warnings on all "Sony Corporation"
++ *
++ * _OSI(Linux) is a NOP:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SZ650N"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SZ38GP_C"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-TZ21MN_N"),
++ * _OSI(Linux) unknown effect:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ11M"),
++ */
++ {
++ .callback = dmi_unknown_osi_linux,
++ .ident = "Sony",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
++ },
++ },
++ /*
++ * Disable OSI(Linux) warnings on all "TOSHIBA"
++ *
++ * _OSI(Linux) breaks sound (bugzilla 7787):
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P100"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P105"),
++ * _OSI(Linux) is a NOP:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A100"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A210"),
++ * _OSI(Linux) unknown effect:
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A135"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A200"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P205"),
++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite U305"),
++ */
++ {
++ .callback = dmi_disable_osi_linux,
++ .ident = "Toshiba",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
++ },
++ },
++ {}
++};
++
++#endif /* CONFIG_DMI */
+--- a/drivers/acpi/osl.c
++++ b/drivers/acpi/osl.c
+@@ -77,11 +77,55 @@ static struct workqueue_struct *kacpi_no
+ #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */
+ static char osi_additional_string[OSI_STRING_LENGTH_MAX];
+
+-static int osi_linux; /* disable _OSI(Linux) by default */
++/*
++ * "Ode to _OSI(Linux)"
++ *
++ * osi_linux -- Control response to BIOS _OSI(Linux) query.
++ *
++ * As Linux evolves, the features that it supports change.
++ * So an OSI string such as "Linux" is not specific enough
++ * to be useful across multiple versions of Linux. It
++ * doesn't identify any particular feature, interface,
++ * or even any particular version of Linux...
++ *
++ * Unfortunately, Linux-2.6.22 and earlier responded "yes"
++ * to a BIOS _OSI(Linux) query. When
++ * a reference mobile BIOS started using it, its use
++ * started to spread to many vendor platforms.
++ * As it is not supportable, we need to halt that spread.
++ *
++ * Today, most BIOS references to _OSI(Linux) are noise --
++ * they have no functional effect and are just dead code
++ * carried over from the reference BIOS.
++ *
++ * The next most common case is that _OSI(Linux) harms Linux,
++ * usually by causing the BIOS to follow paths that are
++ * not tested during Windows validation.
++ *
++ * Finally, there is a short list of platforms
++ * where OSI(Linux) benefits Linux.
++ *
++ * In Linux-2.6.23, OSI(Linux) is first disabled by default.
++ * DMI is used to disable the dmesg warning about OSI(Linux)
++ * on platforms where it is known to have no effect.
++ * But a dmesg warning remains for systems where
++ * we do not know if OSI(Linux) is good or bad for the system.
++ * DMI is also used to enable OSI(Linux) for the machines
++ * that are known to need it.
++ *
++ * BIOS writers should NOT query _OSI(Linux) on future systems.
++ * It will be ignored by default, and to get Linux to
++ * not ignore it will require a kernel source update to
++ * add a DMI entry, or a boot-time "acpi_osi=Linux" invocation.
++ */
++#define OSI_LINUX_ENABLE 0
+
+-#ifdef CONFIG_DMI
+-static struct __initdata dmi_system_id acpi_osl_dmi_table[];
+-#endif
++static struct osi_linux {
++ unsigned int enable:1;
++ unsigned int dmi:1;
++ unsigned int cmdline:1;
++ unsigned int known:1;
++} osi_linux = { OSI_LINUX_ENABLE, 0, 0, 0};
+
+ static void __init acpi_request_region (struct acpi_generic_address *addr,
+ unsigned int length, char *desc)
+@@ -133,7 +177,6 @@ device_initcall(acpi_reserve_resources);
+
+ acpi_status __init acpi_os_initialize(void)
+ {
+- dmi_check_system(acpi_osl_dmi_table);
+ return AE_OK;
+ }
+
+@@ -971,13 +1014,37 @@ static int __init acpi_os_name_setup(cha
+
+ __setup("acpi_os_name=", acpi_os_name_setup);
+
+-static void enable_osi_linux(int enable) {
++static void __init set_osi_linux(unsigned int enable)
++{
++ if (osi_linux.enable != enable) {
++ osi_linux.enable = enable;
++ printk(KERN_NOTICE PREFIX "%sed _OSI(Linux)\n",
++ enable ? "Add": "Delet");
++ }
++ return;
++}
+
+- if (osi_linux != enable)
+- printk(KERN_INFO PREFIX "%sabled _OSI(Linux)\n",
+- enable ? "En": "Dis");
++static void __init acpi_cmdline_osi_linux(unsigned int enable)
++{
++ osi_linux.cmdline = 1; /* cmdline set the default */
++ set_osi_linux(enable);
++
++ return;
++}
++
++void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d)
++{
++ osi_linux.dmi = 1; /* DMI knows that this box asks OSI(Linux) */
++
++ printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident);
++
++ if (enable == -1)
++ return;
++
++ osi_linux.known = 1; /* DMI knows which OSI(Linux) default needed */
++
++ set_osi_linux(enable);
+
+- osi_linux = enable;
+ return;
+ }
+
+@@ -994,12 +1061,12 @@ static int __init acpi_osi_setup(char *s
+ printk(KERN_INFO PREFIX "_OSI method disabled\n");
+ acpi_gbl_create_osi_method = FALSE;
+ } else if (!strcmp("!Linux", str)) {
+- enable_osi_linux(0);
++ acpi_cmdline_osi_linux(0); /* !enable */
+ } else if (*str == '!') {
+ if (acpi_osi_invalidate(++str) == AE_OK)
+ printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str);
+ } else if (!strcmp("Linux", str)) {
+- enable_osi_linux(1);
++ acpi_cmdline_osi_linux(1); /* enable */
+ } else if (*osi_additional_string == '\0') {
+ strncpy(osi_additional_string, str, OSI_STRING_LENGTH_MAX);
+ printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str);
+@@ -1156,6 +1223,34 @@ acpi_status acpi_os_release_object(acpi_
+ return (AE_OK);
+ }
+
++/**
++ * acpi_dmi_dump - dump DMI slots needed for blacklist entry
++ *
++ * Returns 0 on success
++ */
++static int acpi_dmi_dump(void)
++{
++
++ if (!dmi_available)
++ return -1;
++
++ printk(KERN_NOTICE PREFIX "DMI System Vendor: %s\n",
++ dmi_get_system_info(DMI_SYS_VENDOR));
++ printk(KERN_NOTICE PREFIX "DMI Product Name: %s\n",
++ dmi_get_system_info(DMI_PRODUCT_NAME));
++ printk(KERN_NOTICE PREFIX "DMI Product Version: %s\n",
++ dmi_get_system_info(DMI_PRODUCT_VERSION));
++ printk(KERN_NOTICE PREFIX "DMI Board Name: %s\n",
++ dmi_get_system_info(DMI_BOARD_NAME));
++ printk(KERN_NOTICE PREFIX "DMI BIOS Vendor: %s\n",
++ dmi_get_system_info(DMI_BIOS_VENDOR));
++ printk(KERN_NOTICE PREFIX "DMI BIOS Date: %s\n",
++ dmi_get_system_info(DMI_BIOS_DATE));
++
++ return 0;
++}
++
++
+ /******************************************************************************
+ *
+ * FUNCTION: acpi_os_validate_interface
+@@ -1175,13 +1270,29 @@ acpi_os_validate_interface (char *interf
+ if (!strncmp(osi_additional_string, interface, OSI_STRING_LENGTH_MAX))
+ return AE_OK;
+ if (!strcmp("Linux", interface)) {
+- printk(KERN_WARNING PREFIX
+- "System BIOS is requesting _OSI(Linux)\n");
+- printk(KERN_WARNING PREFIX
+- "If \"acpi_osi=Linux\" works better,\n"
+- "Please send dmidecode "
+- "to linux-acpi@vger.kernel.org\n");
+- if(osi_linux)
++
++ printk(KERN_NOTICE PREFIX
++ "BIOS _OSI(Linux) query %s%s\n",
++ osi_linux.enable ? "honored" : "ignored",
++ osi_linux.cmdline ? " via cmdline" :
++ osi_linux.dmi ? " via DMI" : "");
++
++ if (!osi_linux.dmi) {
++ if (acpi_dmi_dump())
++ printk(KERN_NOTICE PREFIX
++ "[please extract dmidecode output]\n");
++ printk(KERN_NOTICE PREFIX
++ "Please send DMI info above to "
++ "linux-acpi@vger.kernel.org\n");
++ }
++ if (!osi_linux.known && !osi_linux.cmdline) {
++ printk(KERN_NOTICE PREFIX
++ "If \"acpi_osi=%sLinux\" works better, "
++ "please notify linux-acpi@vger.kernel.org\n",
++ osi_linux.enable ? "!" : "");
++ }
++
++ if (osi_linux.enable)
+ return AE_OK;
+ }
+ return AE_SUPPORT;
+@@ -1213,28 +1324,4 @@ acpi_os_validate_address (
+ return AE_OK;
+ }
+
+-#ifdef CONFIG_DMI
+-static int dmi_osi_linux(struct dmi_system_id *d)
+-{
+- printk(KERN_NOTICE "%s detected: enabling _OSI(Linux)\n", d->ident);
+- enable_osi_linux(1);
+- return 0;
+-}
+-
+-static struct dmi_system_id acpi_osl_dmi_table[] __initdata = {
+- /*
+- * Boxes that need _OSI(Linux)
+- */
+- {
+- .callback = dmi_osi_linux,
+- .ident = "Intel Napa CRB",
+- .matches = {
+- DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
+- DMI_MATCH(DMI_BOARD_NAME, "MPAD-MSAE Customer Reference Boards"),
+- },
+- },
+- {}
+-};
+-#endif /* CONFIG_DMI */
+-
+ #endif
+--- a/drivers/firmware/dmi-id.c
++++ b/drivers/firmware/dmi-id.c
+@@ -159,8 +159,6 @@ static struct device *dmi_dev;
+ if (dmi_get_system_info(_field)) \
+ sys_dmi_attributes[i++] = & sys_dmi_##_name##_attr.attr;
+
+-extern int dmi_available;
+-
+ static int __init dmi_id_init(void)
+ {
+ int ret, i;
+--- a/include/linux/acpi.h
++++ b/include/linux/acpi.h
+@@ -40,6 +40,7 @@
+ #include <acpi/acpi_drivers.h>
+ #include <acpi/acpi_numa.h>
+ #include <asm/acpi.h>
++#include <linux/dmi.h>
+
+
+ #ifdef CONFIG_ACPI
+@@ -187,7 +188,9 @@ extern int ec_transaction(u8 command,
+ #endif /*CONFIG_ACPI_EC*/
+
+ extern int acpi_blacklisted(void);
+-extern void acpi_bios_year(char *s);
++#ifdef CONFIG_DMI
++extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d);
++#endif
+
+ #define ACPI_CSTATE_LIMIT_DEFINED /* for driver builds */
+ #ifdef CONFIG_ACPI
+@@ -247,5 +250,5 @@ static inline int acpi_boot_table_init(v
+ return 0;
+ }
+
+-#endif /* CONFIG_ACPI */
++#endif /* !CONFIG_ACPI */
+ #endif /*_LINUX_ACPI_H*/
+--- a/include/linux/dmi.h
++++ b/include/linux/dmi.h
+@@ -78,6 +78,7 @@ extern struct dmi_device * dmi_find_devi
+ extern void dmi_scan_machine(void);
+ extern int dmi_get_year(int field);
+ extern int dmi_name_in_vendors(char *str);
++extern int dmi_available;
+
+ #else
+
+@@ -87,6 +88,7 @@ static inline struct dmi_device * dmi_fi
+ struct dmi_device *from) { return NULL; }
+ static inline int dmi_get_year(int year) { return 0; }
+ static inline int dmi_name_in_vendors(char *s) { return 0; }
++#define dmi_available 0
+
+ #endif
+
--- /dev/null
+From stable-bounces@linux.kernel.org Wed Jan 30 18:11:36 2008
+From: Jay Cliburn <jacliburn@bellsouth.net>
+Date: Wed, 30 Jan 2008 20:11:08 -0600
+Subject: atl1: fix frame length bug
+To: greg@kroah.com, chrisw@sous-sol.org
+Cc: david.harris@cpni-inc.com, csnook@redhat.com, stable@kernel.org, jeff@garzik.org, linux-kernel@vger.kernel.org
+Message-ID: <20080130201108.75878288@osprey.hogchain.net>
+
+From: Jay Cliburn <jacliburn@bellsouth.net>
+
+
+Upstream commit: 2a49128f0a6edee337174ea341c1d6d7565be350
+
+The driver sets up the hardware to accept a frame with max length
+equal to MTU + Ethernet header + FCS + VLAN tag, but we neglect to
+add the VLAN tag size to the ingress buffer. When a VLAN-tagged
+frame arrives, the hardware passes it, but bad things happen
+because the buffer is too small. This patch fixes that.
+
+Thanks to David Harris for reporting the bug and testing the fix.
+
+Signed-off-by: Jay Cliburn <jacliburn@bellsouth.net>
+Tested-by: David Harris <david.harris@cpni-inc.com>
+Signed-off-by: Jeff Garzik <jeff@garzik.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/net/atl1/atl1_main.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/atl1/atl1_main.c
++++ b/drivers/net/atl1/atl1_main.c
+@@ -121,7 +121,7 @@ static int __devinit atl1_sw_init(struct
+ struct atl1_hw *hw = &adapter->hw;
+ struct net_device *netdev = adapter->netdev;
+
+- hw->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN;
++ hw->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+ hw->min_frame_size = ETH_ZLEN + ETH_FCS_LEN;
+
+ adapter->wol = 0;
+@@ -689,7 +689,7 @@ static int atl1_change_mtu(struct net_de
+ {
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ int old_mtu = netdev->mtu;
+- int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
++ int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+
+ if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) ||
+ (max_frame > MAX_JUMBO_FRAME_SIZE)) {
+@@ -854,8 +854,8 @@ static u32 atl1_configure(struct atl1_ad
+ /* set Interrupt Clear Timer */
+ iowrite16(adapter->ict, hw->hw_addr + REG_CMBDISDMA_TIMER);
+
+- /* set MTU, 4 : VLAN */
+- iowrite32(hw->max_frame_size + 4, hw->hw_addr + REG_MTU);
++ /* set max frame size hw will accept */
++ iowrite32(hw->max_frame_size, hw->hw_addr + REG_MTU);
+
+ /* jumbo size & rrd retirement timer */
+ value = (((u32) hw->rx_jumbo_th & RXQ_JMBOSZ_TH_MASK)
--- /dev/null
+From stable-bounces@linux.kernel.org Sun Feb 3 15:29:36 2008
+From: Björn Steinbrink <B.Steinbrink@gmx.de>
+Date: Sun, 3 Feb 2008 23:29:12 +0000
+Subject: Fix dirty page accounting leak with ext3 data=journal
+To: stable@kernel.org
+Cc: B.Steinbrink@gmx.de, jack@suse.cz
+Message-ID: <279fbba40802031529s7f13798bic17860c64a21267@mail.gmail.com>
+
+From: Björn Steinbrink <B.Steinbrink@gmx.de>
+
+patch a2b345642f530054a92b8d2b5108436225a8093e in mainline.
+
+In 46d2277c796f9f4937bfa668c40b2e3f43e93dd0, try_to_free_buffers was
+changed to bail out if the page was dirty. That caused
+truncate_complete_page to leak massive amounts of memory, because the
+dirty bit was only cleared after the call to try_to_free_buffers. So the
+call to cancel_dirty_page was moved up to have the dirty bit cleared
+early in 3e67c0987d7567ad666641164a153dca9a43b11d.
+
+The problem with that fix is, that the page can be redirtied after
+cancel_dirty_page was called, eg. like this:
+
+truncate_complete_page()
+ cancel_dirty_page() // PG_dirty cleared, decr. dirty pages
+ do_invalidatepage()
+ ext3_invalidatepage()
+ journal_invalidatepage()
+ journal_unmap_buffer()
+ __dispose_buffer()
+ __journal_unfile_buffer()
+ __journal_temp_unlink_buffer()
+ mark_buffer_dirty(); // PG_dirty set, incr. dirty pages
+
+And then we end up with dirty pages being wrongly accounted.
+
+In ecdfc9787fe527491baefc22dce8b2dbd5b2908d the changes to
+try_to_free_buffers were reverted, so the original reason for the
+massive memory leak is gone, so we can also revert the move of
+the call to cancel_dirty_page from truncate_complete_page and get the
+accounting right again.
+
+Signed-off-by: Björn Steinbrink <B.Steinbrink@gmx.de>
+Tested-by: Krzysztof Piotr Oledzki <ole@ans.pl>
+Tested-by: Zaid D. <zaid.box@gmail.com>
+Cc: Jan Kara <jack@ucw.cz>
+Cc: Nick Piggin <nickpiggin@yahoo.com.au>
+Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
+Cc: Thomas Osterried <osterried@jesse.de>
+Cc: Kerin Millar <kerframil@gmail.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ mm/truncate.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/mm/truncate.c
++++ b/mm/truncate.c
+@@ -95,11 +95,11 @@ truncate_complete_page(struct address_sp
+ if (page->mapping != mapping)
+ return;
+
+- cancel_dirty_page(page, PAGE_CACHE_SIZE);
+-
+ if (PagePrivate(page))
+ do_invalidatepage(page, 0);
+
++ cancel_dirty_page(page, PAGE_CACHE_SIZE);
++
+ remove_from_page_cache(page);
+ ClearPageUptodate(page);
+ ClearPageMappedToDisk(page);
--- /dev/null
+From kkeil@suse.de Fri Jan 25 04:42:25 2008
+From: Karsten Keil <kkeil@suse.de>
+Date: Fri, 25 Jan 2008 13:42:23 +0100
+Subject: fix oops on rmmod capidrv
+To: gregkh@suse.de
+Message-ID: <20080125124223.GA10414@pingi.kke.suse.de>
+Content-Disposition: inline
+
+From: Karsten Keil <kkeil@suse.de>
+
+patch eb36f4fc019835cecf0788907f6cab774508087b in mainline.
+
+Fix overwriting the stack with the version string
+(it is currently 10 bytes + zero) when unloading the
+capidrv module. Safeguard against overwriting it
+should the version string grow in the future.
+
+Should fix Kernel Bug Tracker Bug 9696.
+
+Signed-off-by: Gerd v. Egidy <gerd.von.egidy@intra2net.com>
+Acked-by: Karsten Keil <kkeil@suse.de>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/isdn/capi/capidrv.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/drivers/isdn/capi/capidrv.c
++++ b/drivers/isdn/capi/capidrv.c
+@@ -2306,13 +2306,14 @@ static int __init capidrv_init(void)
+
+ static void __exit capidrv_exit(void)
+ {
+- char rev[10];
++ char rev[32];
+ char *p;
+
+ if ((p = strchr(revision, ':')) != 0) {
+- strcpy(rev, p + 1);
+- p = strchr(rev, '$');
+- *p = 0;
++ strncpy(rev, p + 1, sizeof(rev));
++ rev[sizeof(rev)-1] = 0;
++ if ((p = strchr(rev, '$')) != 0)
++ *p = 0;
+ } else {
+ strcpy(rev, " ??? ");
+ }
--- /dev/null
+From stable-bounces@linux.kernel.org Thu Jan 17 15:30:17 2008
+From: akpm@linux-foundation.org
+Date: Thu, 17 Jan 2008 15:21:21 -0800
+Subject: Fix unbalanced helper_lock in kernel/kmod.c
+To: torvalds@linux-foundation.org
+Cc: akpm@linux-foundation.org, nigel@nigel.suspend2.net, stable@kernel.org, nigel@tuxonice.net
+Message-ID: <200801172321.m0HNLLEd019182@imap1.linux-foundation.org>
+
+
+From: Nigel Cunningham <nigel@nigel.suspend2.net>
+
+patch 784680336b616dcc4c17cbd25add3b49c555cdeb in mainline.
+
+call_usermodehelper_exec() has an exit path that can leave the
+helper_lock() call at the top of the routine unbalanced. The attached
+patch fixes this issue.
+
+Signed-off-by: Nigel Cunningham <nigel@tuxonice.net>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ kernel/kmod.c | 13 ++++++-------
+ 1 file changed, 6 insertions(+), 7 deletions(-)
+
+--- a/kernel/kmod.c
++++ b/kernel/kmod.c
+@@ -451,13 +451,11 @@ int call_usermodehelper_exec(struct subp
+ enum umh_wait wait)
+ {
+ DECLARE_COMPLETION_ONSTACK(done);
+- int retval;
++ int retval = 0;
+
+ helper_lock();
+- if (sub_info->path[0] == '\0') {
+- retval = 0;
++ if (sub_info->path[0] == '\0')
+ goto out;
+- }
+
+ if (!khelper_wq || usermodehelper_disabled) {
+ retval = -EBUSY;
+@@ -468,13 +466,14 @@ int call_usermodehelper_exec(struct subp
+ sub_info->wait = wait;
+
+ queue_work(khelper_wq, &sub_info->work);
+- if (wait == UMH_NO_WAIT) /* task has freed sub_info */
+- return 0;
++ if (wait == UMH_NO_WAIT) /* task has freed sub_info */
++ goto unlock;
+ wait_for_completion(&done);
+ retval = sub_info->retval;
+
+- out:
++out:
+ call_usermodehelper_freeinfo(sub_info);
++unlock:
+ helper_unlock();
+ return retval;
+ }
--- /dev/null
+From stable-bounces@linux.kernel.org Tue Jan 29 09:03:00 2008
+From: Ayaz Abdulla <aabdulla@nvidia.com>
+Date: Mon, 28 Jan 2008 10:24:40 -0500
+Subject: forcedeth: mac address mcp77/79
+To: Jeff Garzik <jgarzik@pobox.com>, Andrew Morton <akpm@osdl.org>, nedev <netdev@vger.kernel.org>, stable@kernel.org
+Message-ID: <479DF3B8.2000204@nvidia.com>
+
+From: Ayaz Abdulla <aabdulla@nvidia.com>
+
+patch 2b91213064bd882c3adf35f028c6d12fab3269ec in mainline.
+
+This patch is a critical fix for MCP77 and MCP79 devices. The feature
+flags were missing the define for correct mac address
+(DEV_HAS_CORRECT_MACADDR).
+
+Signed-off-by: Ayaz Abdulla <aabdulla@nvidia.com>
+Signed-off-by: Jeff Garzik <jeff@garzik.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/net/forcedeth.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/forcedeth.c
++++ b/drivers/net/forcedeth.c
+@@ -5564,35 +5564,35 @@ static struct pci_device_id pci_tbl[] =
+ },
+ { /* MCP77 Ethernet Controller */
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_32),
+- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
+ },
+ { /* MCP77 Ethernet Controller */
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_33),
+- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
+ },
+ { /* MCP77 Ethernet Controller */
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_34),
+- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
+ },
+ { /* MCP77 Ethernet Controller */
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_35),
+- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
+ },
+ { /* MCP79 Ethernet Controller */
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_36),
+- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
+ },
+ { /* MCP79 Ethernet Controller */
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_37),
+- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
+ },
+ { /* MCP79 Ethernet Controller */
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_38),
+- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
+ },
+ { /* MCP79 Ethernet Controller */
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_39),
+- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
+ },
+ {0,},
+ };
--- /dev/null
+From stable-bounces@linux.kernel.org Wed Jan 16 11:05:19 2008
+From: "Luck, Tony" <tony.luck@intel.com>
+Date: Wed, 16 Jan 2008 11:04:16 -0800
+Subject: ia64: Fix unaligned handler for floating point instructions with base update
+To: <stable@kernel.org>
+Message-ID: <1FE6DD409037234FAB833C420AA843EC5854E9@orsmsx424.amr.corp.intel.com>
+
+From: Luck, Tony <tony.luck@intel.com>
+
+commit 1a499150e4ec1299232e24389f648d059ce5617a in mainline.
+
+[IA64] Fix unaligned handler for floating point instructions with base update
+
+The compiler team did the hard work for this distilling a problem in
+large fortran application which showed up when applied to a 290MB input
+data set down to this instruction:
+
+ ldfd f34=[r17],-8
+
+Which they noticed incremented r17 by 0x10 rather than decrementing it
+by 8 when the value in r17 caused an unaligned data fault. I tracked
+it down to some bad instruction decoding in unaligned.c. The code
+assumes that the 'x' bit can determine whether the instruction is
+an "ldf" or "ldfp" ... which it is for opcode=6 (see table 4-29 on
+page 3:302 of the SDM). But for opcode=7 the 'x' bit is irrelevent,
+all variants are "ldf" instructions (see table 4-36 on page 3:306).
+
+Note also that interpreting the instruction as "ldfp" means that the
+"paired" floating point register (f35 in the example here) will also
+be corrupted.
+
+Signed-off-by: Tony Luck <tony.luck@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/ia64/kernel/unaligned.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+--- a/arch/ia64/kernel/unaligned.c
++++ b/arch/ia64/kernel/unaligned.c
+@@ -1487,16 +1487,19 @@ ia64_handle_unaligned (unsigned long ifa
+ case LDFA_OP:
+ case LDFCCLR_OP:
+ case LDFCNC_OP:
+- case LDF_IMM_OP:
+- case LDFA_IMM_OP:
+- case LDFCCLR_IMM_OP:
+- case LDFCNC_IMM_OP:
+ if (u.insn.x)
+ ret = emulate_load_floatpair(ifa, u.insn, regs);
+ else
+ ret = emulate_load_float(ifa, u.insn, regs);
+ break;
+
++ case LDF_IMM_OP:
++ case LDFA_IMM_OP:
++ case LDFCCLR_IMM_OP:
++ case LDFCNC_IMM_OP:
++ ret = emulate_load_float(ifa, u.insn, regs);
++ break;
++
+ case STF_OP:
+ case STF_IMM_OP:
+ ret = emulate_store_float(ifa, u.insn, regs);
--- /dev/null
+From stable-bounces@linux.kernel.org Fri Jan 18 04:55:18 2008
+From: Tejun Heo <htejun@gmail.com>
+Date: Fri, 18 Jan 2008 21:52:50 +0900
+Subject: libata: port and host should be stopped before hardware resources are released
+To: stable@kernel.org
+Cc: Tomasz Chmielewski <mangoo@wpkg.org>, Mark Lord <liml@rtr.ca>
+Message-ID: <4790A122.6050101@gmail.com>
+
+From: Tejun Heo <htejun@gmail.com>
+
+This is backport of 32ebbc0c0d5d18c0135b55d1eb0029f48c54aff0 and fixes
+oops on driver module unload.
+
+Port / host stop calls used to be made from ata_host_release() which
+is called after all hardware resources acquired after host allocation
+are released. This is wrong as port and host stop routines often
+access the hardware.
+
+Add separate devres for port / host stop which is invoked right after
+IRQ is released but with all other hardware resources intact. The
+devres is added iff ->host_stop and/or ->port_stop exist.
+
+This problem has been spotted by Mark Lord.
+
+Signed-off-by: Tejun Heo <htejun@gmail.com>
+Cc: Mark Lord <liml@rtr.ca>
+Signed-off-by: Jeff Garzik <jeff@garzik.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/ata/libata-core.c | 52 ++++++++++++++++++++++++++++++++++------------
+ 1 file changed, 39 insertions(+), 13 deletions(-)
+
+--- a/drivers/ata/libata-core.c
++++ b/drivers/ata/libata-core.c
+@@ -6121,19 +6121,6 @@ static void ata_host_release(struct devi
+ if (!ap)
+ continue;
+
+- if ((host->flags & ATA_HOST_STARTED) && ap->ops->port_stop)
+- ap->ops->port_stop(ap);
+- }
+-
+- if ((host->flags & ATA_HOST_STARTED) && host->ops->host_stop)
+- host->ops->host_stop(host);
+-
+- for (i = 0; i < host->n_ports; i++) {
+- struct ata_port *ap = host->ports[i];
+-
+- if (!ap)
+- continue;
+-
+ if (ap->scsi_host)
+ scsi_host_put(ap->scsi_host);
+
+@@ -6258,6 +6245,24 @@ struct ata_host *ata_host_alloc_pinfo(st
+ return host;
+ }
+
++static void ata_host_stop(struct device *gendev, void *res)
++{
++ struct ata_host *host = dev_get_drvdata(gendev);
++ int i;
++
++ WARN_ON(!(host->flags & ATA_HOST_STARTED));
++
++ for (i = 0; i < host->n_ports; i++) {
++ struct ata_port *ap = host->ports[i];
++
++ if (ap->ops->port_stop)
++ ap->ops->port_stop(ap);
++ }
++
++ if (host->ops->host_stop)
++ host->ops->host_stop(host);
++}
++
+ /**
+ * ata_host_start - start and freeze ports of an ATA host
+ * @host: ATA host to start ports for
+@@ -6276,6 +6281,8 @@ struct ata_host *ata_host_alloc_pinfo(st
+ */
+ int ata_host_start(struct ata_host *host)
+ {
++ int have_stop = 0;
++ void *start_dr = NULL;
+ int i, rc;
+
+ if (host->flags & ATA_HOST_STARTED)
+@@ -6287,6 +6294,22 @@ int ata_host_start(struct ata_host *host
+ if (!host->ops && !ata_port_is_dummy(ap))
+ host->ops = ap->ops;
+
++ if (ap->ops->port_stop)
++ have_stop = 1;
++ }
++
++ if (host->ops->host_stop)
++ have_stop = 1;
++
++ if (have_stop) {
++ start_dr = devres_alloc(ata_host_stop, 0, GFP_KERNEL);
++ if (!start_dr)
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < host->n_ports; i++) {
++ struct ata_port *ap = host->ports[i];
++
+ if (ap->ops->port_start) {
+ rc = ap->ops->port_start(ap);
+ if (rc) {
+@@ -6299,6 +6322,8 @@ int ata_host_start(struct ata_host *host
+ ata_eh_freeze_port(ap);
+ }
+
++ if (start_dr)
++ devres_add(host->dev, start_dr);
+ host->flags |= ATA_HOST_STARTED;
+ return 0;
+
+@@ -6309,6 +6334,7 @@ int ata_host_start(struct ata_host *host
+ if (ap->ops->port_stop)
+ ap->ops->port_stop(ap);
+ }
++ devres_free(start_dr);
+ return rc;
+ }
+
--- /dev/null
+From stable-bounces@linux.kernel.org Tue Jan 29 10:08:57 2008
+From: Patrick McHardy <kaber@trash.net>
+Date: Tue, 29 Jan 2008 19:08:25 +0100
+Subject: Netfilter: bridge: fix double POST_ROUTING invocation
+To: stable@kernel.org
+Cc: Netfilter Development Mailinglist <netfilter-devel@vger.kernel.org>, "David S. Miller" <davem@davemloft.net>
+Message-ID: <479F6B99.60005@trash.net>
+
+From: Patrick McHardy <kaber@trash.net>
+
+[NETFILTER]: bridge: fix double POST_ROUTING invocation
+
+Upstream commit 2948d2ebbb98747b912ac6d0c864b4d02be8a6f5
+
+The bridge code incorrectly causes two POST_ROUTING hook invocations
+for DNATed packets that end up on the same bridge device. This
+happens because packets with a changed destination address are passed
+to dst_output() to make them go through the neighbour output function
+again to build a new destination MAC address, before they will continue
+through the IP hooks simulated by bridge netfilter.
+
+The resulting hook order is:
+ PREROUTING (bridge netfilter)
+ POSTROUTING (dst_output -> ip_output)
+ FORWARD (bridge netfilter)
+ POSTROUTING (bridge netfilter)
+
+The deferred hooks used to abort the first POST_ROUTING invocation,
+but since the only thing bridge netfilter actually really wants is
+a new MAC address, we can avoid going through the IP stack completely
+by simply calling the neighbour output function directly.
+
+Tested, reported and lots of data provided by: Damien Thebault <damien.thebault@gmail.com>
+
+Signed-off-by: Patrick McHardy <kaber@trash.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ net/bridge/br_netfilter.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+--- a/net/bridge/br_netfilter.c
++++ b/net/bridge/br_netfilter.c
+@@ -247,8 +247,9 @@ static void __br_dnat_complain(void)
+ * Let us first consider the case that ip_route_input() succeeds:
+ *
+ * If skb->dst->dev equals the logical bridge device the packet
+- * came in on, we can consider this bridging. We then call
+- * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * came in on, we can consider this bridging. The packet is passed
++ * through the neighbour output function to build a new destination
++ * MAC address, which will make the packet enter br_nf_local_out()
+ * not much later. In that function it is assured that the iptables
+ * FORWARD chain is traversed for the packet.
+ *
+@@ -285,12 +286,17 @@ static int br_nf_pre_routing_finish_brid
+ skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
+
+ skb->dev = bridge_parent(skb->dev);
+- if (!skb->dev)
+- kfree_skb(skb);
+- else {
++ if (skb->dev) {
++ struct dst_entry *dst = skb->dst;
++
+ nf_bridge_pull_encap_header(skb);
+- skb->dst->output(skb);
++
++ if (dst->hh)
++ return neigh_hh_output(dst->hh, skb);
++ else if (dst->neighbour)
++ return dst->neighbour->output(skb);
+ }
++ kfree_skb(skb);
+ return 0;
+ }
+
--- /dev/null
+From stable-bounces@linux.kernel.org Tue Jan 29 10:09:08 2008
+From: Patrick McHardy <kaber@trash.net>
+Date: Tue, 29 Jan 2008 19:08:28 +0100
+Subject: Netfilter: bridge-netfilter: fix net_device refcnt leaks
+To: stable@kernel.org
+Cc: Netfilter Development Mailinglist <netfilter-devel@vger.kernel.org>, "David S. Miller" <davem@davemloft.net>
+Message-ID: <479F6B9C.6000101@trash.net>
+
+
+From: Patrick McHardy <kaber@trash.net>
+
+[NETFILTER]: bridge-netfilter: fix net_device refcnt leaks
+
+Upstream commit 2dc2f207fb251666d2396fe1a69272b307ecc333
+
+When packets are flood-forwarded to multiple output devices, the
+bridge-netfilter code reuses skb->nf_bridge for each clone to store
+the bridge port. When queueing packets using NFQUEUE netfilter takes
+a reference to skb->nf_bridge->physoutdev, which is overwritten
+when the packet is forwarded to the second port. This causes
+refcount unterflows for the first device and refcount leaks for all
+others. Additionally this provides incorrect data to the iptables
+physdev match.
+
+Unshare skb->nf_bridge by copying it if it is shared before assigning
+the physoutdev device.
+
+Reported, tested and based on initial patch by
+Jan Christoph Nordholz <hesso@pool.math.tu-berlin.de>.
+
+Signed-off-by: Patrick McHardy <kaber@trash.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ net/bridge/br_netfilter.c | 27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+--- a/net/bridge/br_netfilter.c
++++ b/net/bridge/br_netfilter.c
+@@ -142,6 +142,23 @@ static inline struct nf_bridge_info *nf_
+ return skb->nf_bridge;
+ }
+
++static inline struct nf_bridge_info *nf_bridge_unshare(struct sk_buff *skb)
++{
++ struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++ if (atomic_read(&nf_bridge->use) > 1) {
++ struct nf_bridge_info *tmp = nf_bridge_alloc(skb);
++
++ if (tmp) {
++ memcpy(tmp, nf_bridge, sizeof(struct nf_bridge_info));
++ atomic_set(&tmp->use, 1);
++ nf_bridge_put(nf_bridge);
++ }
++ nf_bridge = tmp;
++ }
++ return nf_bridge;
++}
++
+ static inline void nf_bridge_push_encap_header(struct sk_buff *skb)
+ {
+ unsigned int len = nf_bridge_encap_header_len(skb);
+@@ -644,6 +661,11 @@ static unsigned int br_nf_forward_ip(uns
+ if (!skb->nf_bridge)
+ return NF_ACCEPT;
+
++ /* Need exclusive nf_bridge_info since we might have multiple
++ * different physoutdevs. */
++ if (!nf_bridge_unshare(skb))
++ return NF_DROP;
++
+ parent = bridge_parent(out);
+ if (!parent)
+ return NF_DROP;
+@@ -727,6 +749,11 @@ static unsigned int br_nf_local_out(unsi
+ if (!skb->nf_bridge)
+ return NF_ACCEPT;
+
++ /* Need exclusive nf_bridge_info since we might have multiple
++ * different physoutdevs. */
++ if (!nf_bridge_unshare(skb))
++ return NF_DROP;
++
+ nf_bridge = skb->nf_bridge;
+ if (!(nf_bridge->mask & BRNF_BRIDGED_DNAT))
+ return NF_ACCEPT;
--- /dev/null
+From stable-bounces@linux.kernel.org Mon Feb 4 05:53:23 2008
+From: Ian Abbott <abbotti@mev.co.uk>
+Date: Mon, 04 Feb 2008 13:52:38 +0000
+Subject: PCI: Fix fakephp deadlock
+To: stable@kernel.org
+Cc: linux-pci@atrey.karlin.mff.cuni.cz, linux-kernel@vger.kernel.org
+Message-ID: <47A718A6.9080901@mev.co.uk>
+
+
+From: Ian Abbott <abbotti@mev.co.uk>
+
+This patch works around a problem in the fakephp driver when a process
+writing "0" to a "power" sysfs file to fake removal of a PCI device ends
+up deadlocking itself in the sysfs code.
+
+The patch is functionally identical to the one in Linus' tree post 2.6.24:
+http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=5c796ae7a7ebe56967ed9b9963d7c16d733635ff
+
+I have tested it on a 2.6.23 kernel.
+
+Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/pci/hotplug/fakephp.c | 39 +++++++++++++++++++++++++++++++++++----
+ 1 file changed, 35 insertions(+), 4 deletions(-)
+
+--- a/drivers/pci/hotplug/fakephp.c
++++ b/drivers/pci/hotplug/fakephp.c
+@@ -39,6 +39,7 @@
+ #include <linux/init.h>
+ #include <linux/string.h>
+ #include <linux/slab.h>
++#include <linux/workqueue.h>
+ #include "../pci.h"
+
+ #if !defined(MODULE)
+@@ -63,10 +64,16 @@ struct dummy_slot {
+ struct list_head node;
+ struct hotplug_slot *slot;
+ struct pci_dev *dev;
++ struct work_struct remove_work;
++ unsigned long removed;
+ };
+
+ static int debug;
+ static LIST_HEAD(slot_list);
++static struct workqueue_struct *dummyphp_wq;
++
++static void pci_rescan_worker(struct work_struct *work);
++static DECLARE_WORK(pci_rescan_work, pci_rescan_worker);
+
+ static int enable_slot (struct hotplug_slot *slot);
+ static int disable_slot (struct hotplug_slot *slot);
+@@ -109,7 +116,7 @@ static int add_slot(struct pci_dev *dev)
+ slot->name = &dev->dev.bus_id[0];
+ dbg("slot->name = %s\n", slot->name);
+
+- dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
++ dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL);
+ if (!dslot)
+ goto error_info;
+
+@@ -164,6 +171,14 @@ static void remove_slot(struct dummy_slo
+ err("Problem unregistering a slot %s\n", dslot->slot->name);
+ }
+
++/* called from the single-threaded workqueue handler to remove a slot */
++static void remove_slot_worker(struct work_struct *work)
++{
++ struct dummy_slot *dslot =
++ container_of(work, struct dummy_slot, remove_work);
++ remove_slot(dslot);
++}
++
+ /**
+ * Rescan slot.
+ * Tries hard not to re-enable already existing devices
+@@ -267,11 +282,17 @@ static inline void pci_rescan(void) {
+ pci_rescan_buses(&pci_root_buses);
+ }
+
++/* called from the single-threaded workqueue handler to rescan all pci buses */
++static void pci_rescan_worker(struct work_struct *work)
++{
++ pci_rescan();
++}
+
+ static int enable_slot(struct hotplug_slot *hotplug_slot)
+ {
+ /* mis-use enable_slot for rescanning of the pci bus */
+- pci_rescan();
++ cancel_work_sync(&pci_rescan_work);
++ queue_work(dummyphp_wq, &pci_rescan_work);
+ return -ENODEV;
+ }
+
+@@ -306,6 +327,10 @@ static int disable_slot(struct hotplug_s
+ err("Can't remove PCI devices with other PCI devices behind it yet.\n");
+ return -ENODEV;
+ }
++ if (test_and_set_bit(0, &dslot->removed)) {
++ dbg("Slot already scheduled for removal\n");
++ return -ENODEV;
++ }
+ /* search for subfunctions and disable them first */
+ if (!(dslot->dev->devfn & 7)) {
+ for (func = 1; func < 8; func++) {
+@@ -328,8 +353,9 @@ static int disable_slot(struct hotplug_s
+ /* remove the device from the pci core */
+ pci_remove_bus_device(dslot->dev);
+
+- /* blow away this sysfs entry and other parts. */
+- remove_slot(dslot);
++ /* queue work item to blow away this sysfs entry and other parts. */
++ INIT_WORK(&dslot->remove_work, remove_slot_worker);
++ queue_work(dummyphp_wq, &dslot->remove_work);
+
+ return 0;
+ }
+@@ -340,6 +366,7 @@ static void cleanup_slots (void)
+ struct list_head *next;
+ struct dummy_slot *dslot;
+
++ destroy_workqueue(dummyphp_wq);
+ list_for_each_safe (tmp, next, &slot_list) {
+ dslot = list_entry (tmp, struct dummy_slot, node);
+ remove_slot(dslot);
+@@ -351,6 +378,10 @@ static int __init dummyphp_init(void)
+ {
+ info(DRIVER_DESC "\n");
+
++ dummyphp_wq = create_singlethread_workqueue(MY_NAME);
++ if (!dummyphp_wq)
++ return -ENOMEM;
++
+ return pci_scan_buses();
+ }
+
--- /dev/null
+From stable-bounces@linux.kernel.org Wed Jan 16 01:33:32 2008
+From: Mikael Pettersson <mikpe@it.uu.se>
+Date: Wed, 16 Jan 2008 10:33:00 +0100 (MET)
+Subject: sata_promise: ASIC PRD table bug workaround
+To: stable@kernel.org
+Cc: Jeff Garzik <jeff@garzik.org>
+Message-ID: <200801160933.m0G9X0Y9029901@harpo.it.uu.se>
+
+From: Mikael Pettersson <mikpe@it.uu.se>
+
+patch 03116d67e0973bb493fe9307e28973a24a272bcc in mainline.
+
+Second-generation Promise SATA controllers have an ASIC bug
+which can trigger if the last PRD entry is larger than 164 bytes,
+resulting in intermittent errors and possible data corruption.
+
+Work around this by replacing calls to ata_qc_prep() with a
+private version that fills the PRD, checks the size of the
+last entry, and if necessary splits it to avoid the bug.
+Also reduce sg_tablesize by 1 to accommodate the new entry.
+
+Tested on the second-generation SATA300 TX4 and SATA300 TX2plus,
+and the first-generation PDC20378.
+
+Thanks to Alexander Sabourenkov for verifying the bug by
+studying the vendor driver, and for writing the initial patch
+upon which this one is based.
+
+Signed-off-by: Mikael Pettersson <mikpe@it.uu.se>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/ata/sata_promise.c | 87 ++++++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 83 insertions(+), 4 deletions(-)
+
+--- a/drivers/ata/sata_promise.c
++++ b/drivers/ata/sata_promise.c
+@@ -50,6 +50,7 @@
+ enum {
+ PDC_MAX_PORTS = 4,
+ PDC_MMIO_BAR = 3,
++ PDC_MAX_PRD = LIBATA_MAX_PRD - 1, /* -1 for ASIC PRD bug workaround */
+
+ /* register offsets */
+ PDC_FEATURE = 0x04, /* Feature/Error reg (per port) */
+@@ -155,7 +156,7 @@ static struct scsi_host_template pdc_ata
+ .queuecommand = ata_scsi_queuecmd,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+- .sg_tablesize = LIBATA_MAX_PRD,
++ .sg_tablesize = PDC_MAX_PRD,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+@@ -527,6 +528,84 @@ static void pdc_atapi_pkt(struct ata_que
+ memcpy(buf+31, cdb, cdb_len);
+ }
+
++/**
++ * pdc_fill_sg - Fill PCI IDE PRD table
++ * @qc: Metadata associated with taskfile to be transferred
++ *
++ * Fill PCI IDE PRD (scatter-gather) table with segments
++ * associated with the current disk command.
++ * Make sure hardware does not choke on it.
++ *
++ * LOCKING:
++ * spin_lock_irqsave(host lock)
++ *
++ */
++static void pdc_fill_sg(struct ata_queued_cmd *qc)
++{
++ struct ata_port *ap = qc->ap;
++ struct scatterlist *sg;
++ unsigned int idx;
++ const u32 SG_COUNT_ASIC_BUG = 41*4;
++
++ if (!(qc->flags & ATA_QCFLAG_DMAMAP))
++ return;
++
++ WARN_ON(qc->__sg == NULL);
++ WARN_ON(qc->n_elem == 0 && qc->pad_len == 0);
++
++ idx = 0;
++ ata_for_each_sg(sg, qc) {
++ u32 addr, offset;
++ u32 sg_len, len;
++
++ /* determine if physical DMA addr spans 64K boundary.
++ * Note h/w doesn't support 64-bit, so we unconditionally
++ * truncate dma_addr_t to u32.
++ */
++ addr = (u32) sg_dma_address(sg);
++ sg_len = sg_dma_len(sg);
++
++ while (sg_len) {
++ offset = addr & 0xffff;
++ len = sg_len;
++ if ((offset + sg_len) > 0x10000)
++ len = 0x10000 - offset;
++
++ ap->prd[idx].addr = cpu_to_le32(addr);
++ ap->prd[idx].flags_len = cpu_to_le32(len & 0xffff);
++ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len);
++
++ idx++;
++ sg_len -= len;
++ addr += len;
++ }
++ }
++
++ if (idx) {
++ u32 len = le32_to_cpu(ap->prd[idx - 1].flags_len);
++
++ if (len > SG_COUNT_ASIC_BUG) {
++ u32 addr;
++
++ VPRINTK("Splitting last PRD.\n");
++
++ addr = le32_to_cpu(ap->prd[idx - 1].addr);
++ ap->prd[idx - 1].flags_len = cpu_to_le32(len - SG_COUNT_ASIC_BUG);
++ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx - 1, addr, SG_COUNT_ASIC_BUG);
++
++ addr = addr + len - SG_COUNT_ASIC_BUG;
++ len = SG_COUNT_ASIC_BUG;
++ ap->prd[idx].addr = cpu_to_le32(addr);
++ ap->prd[idx].flags_len = cpu_to_le32(len);
++ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len);
++
++ idx++;
++ }
++
++ ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
++ }
++}
++
+ static void pdc_qc_prep(struct ata_queued_cmd *qc)
+ {
+ struct pdc_port_priv *pp = qc->ap->private_data;
+@@ -536,7 +615,7 @@ static void pdc_qc_prep(struct ata_queue
+
+ switch (qc->tf.protocol) {
+ case ATA_PROT_DMA:
+- ata_qc_prep(qc);
++ pdc_fill_sg(qc);
+ /* fall through */
+
+ case ATA_PROT_NODATA:
+@@ -552,11 +631,11 @@ static void pdc_qc_prep(struct ata_queue
+ break;
+
+ case ATA_PROT_ATAPI:
+- ata_qc_prep(qc);
++ pdc_fill_sg(qc);
+ break;
+
+ case ATA_PROT_ATAPI_DMA:
+- ata_qc_prep(qc);
++ pdc_fill_sg(qc);
+ /*FALLTHROUGH*/
+ case ATA_PROT_ATAPI_NODATA:
+ pdc_atapi_pkt(qc);
md-fix-data-corruption-when-a-degraded-raid5-array-is-reshaped.patch
knfsd-allow-nfsv2-3-write-calls-to-succeed-when-krb5i-etc-is-used.patch
vm-audit-add-vm_dontexpand-to-mmap-for-drivers-that-need-it.patch
+sata_promise-asic-prd-table-bug-workaround.patch
+ia64-fix-unaligned-handler-for-floating-point-instructions-with-base-update.patch
+fix-unbalanced-helper_lock-in-kernel-kmod.c.patch
+spi-omap2_mcspi-pio-rx-fix.patch
+libata-port-and-host-should-be-stopped-before-hardware-resources-are-released.patch
+fix-oops-on-rmmod-capidrv.patch
+netfilter-bridge-fix-double-post_routing-invocation.patch
+netfilter-bridge-netfilter-fix-net_device-refcnt-leaks.patch
+fix-dirty-page-accounting-leak-with-ext3-data-journal.patch
+forcedeth-mac-address-mcp77-79.patch
+atl1-fix-frame-length-bug.patch
+acpi-sync-blacklist-w-latest.patch
+pci-fix-fakephp-deadlock.patch
--- /dev/null
+From stable-bounces@linux.kernel.org Thu Jan 24 14:01:15 2008
+From: akpm@linux-foundation.org
+Date: Thu, 24 Jan 2008 14:00:40 -0800
+Subject: spi: omap2_mcspi PIO RX fix
+To: torvalds@linux-foundation.org
+Cc: akpm@linux-foundation.org, dbrownell@users.sourceforge.net, kalle.valo@nokia.com, stable@kernel.org
+Message-ID: <200801242200.m0OM0YEc015614@imap1.linux-foundation.org>
+
+
+From: Kalle Valo <kalle.valo@nokia.com>
+
+patch feed9bab7b14b77be8d796bcee95e2343fb82955 in mainline.
+
+Before transmission of the last word in PIO RX_ONLY mode rx+tx mode
+is enabled:
+
+ /* prevent last RX_ONLY read from triggering
+ * more word i/o: switch to rx+tx
+ */
+ if (c == 0 && tx == NULL)
+ mcspi_write_cs_reg(spi,
+ OMAP2_MCSPI_CHCONF0, l);
+
+But because c is decremented after the test, c will never be zero and
+rx+tx will not be enabled. This breaks RX_ONLY mode PIO transfers.
+
+Fix it by decrementing c in the beginning of the various I/O loops.
+
+Signed-off-by: Kalle Valo <kalle.valo@nokia.com>
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/spi/omap2_mcspi.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/spi/omap2_mcspi.c
++++ b/drivers/spi/omap2_mcspi.c
+@@ -350,6 +350,7 @@ omap2_mcspi_txrx_pio(struct spi_device *
+ tx = xfer->tx_buf;
+
+ do {
++ c -= 1;
+ if (tx != NULL) {
+ if (mcspi_wait_for_reg_bit(chstat_reg,
+ OMAP2_MCSPI_CHSTAT_TXS) < 0) {
+@@ -380,7 +381,6 @@ omap2_mcspi_txrx_pio(struct spi_device *
+ word_len, *(rx - 1));
+ #endif
+ }
+- c -= 1;
+ } while (c);
+ } else if (word_len <= 16) {
+ u16 *rx;
+@@ -389,6 +389,7 @@ omap2_mcspi_txrx_pio(struct spi_device *
+ rx = xfer->rx_buf;
+ tx = xfer->tx_buf;
+ do {
++ c -= 2;
+ if (tx != NULL) {
+ if (mcspi_wait_for_reg_bit(chstat_reg,
+ OMAP2_MCSPI_CHSTAT_TXS) < 0) {
+@@ -419,7 +420,6 @@ omap2_mcspi_txrx_pio(struct spi_device *
+ word_len, *(rx - 1));
+ #endif
+ }
+- c -= 2;
+ } while (c);
+ } else if (word_len <= 32) {
+ u32 *rx;
+@@ -428,6 +428,7 @@ omap2_mcspi_txrx_pio(struct spi_device *
+ rx = xfer->rx_buf;
+ tx = xfer->tx_buf;
+ do {
++ c -= 4;
+ if (tx != NULL) {
+ if (mcspi_wait_for_reg_bit(chstat_reg,
+ OMAP2_MCSPI_CHSTAT_TXS) < 0) {
+@@ -458,7 +459,6 @@ omap2_mcspi_txrx_pio(struct spi_device *
+ word_len, *(rx - 1));
+ #endif
+ }
+- c -= 4;
+ } while (c);
+ }
+