]> Pileus Git - ~andy/linux/commitdiff
Merge tag 'pm+acpi-fixes-3.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 12 Sep 2013 18:22:45 +0000 (11:22 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 12 Sep 2013 18:22:45 +0000 (11:22 -0700)
Pull ACPI and power management fixes from Rafael Wysocki:
 "All of these commits are fixes that have emerged recently and some of
  them fix bugs introduced during this merge window.

  Specifics:

   1) ACPI-based PCI hotplug (ACPIPHP) fixes related to spurious events

      After the recent ACPIPHP changes we've seen some interesting
      breakage on a system that triggers device check notifications
      during boot for non-existing devices.  Although those
      notifications are really spurious, we should be able to deal with
      them nevertheless and that shouldn't introduce too much overhead.
      Four commits to make that work properly.

   2) Memory hotplug and hibernation mutual exclusion rework

      This was maent to be a cleanup, but it happens to fix a classical
      ABBA deadlock between system suspend/hibernation and ACPI memory
      hotplug which is possible if they are started roughly at the same
      time.  Three commits rework memory hotplug so that it doesn't
      acquire pm_mutex and make hibernation use device_hotplug_lock
      which prevents it from racing with memory hotplug.

   3) ACPI Intel LPSS (Low-Power Subsystem) driver crash fix

      The ACPI LPSS driver crashes during boot on Apple Macbook Air with
      Haswell that has slightly unusual BIOS configuration in which one
      of the LPSS device's _CRS method doesn't return all of the
      information expected by the driver.  Fix from Mika Westerberg, for
      stable.

   4) ACPICA fix related to Store->ArgX operation

      AML interpreter fix for obscure breakage that causes AML to be
      executed incorrectly on some machines (observed in practice).
      From Bob Moore.

   5) ACPI core fix for PCI ACPI device objects lookup

      There still are cases in which there is more than one ACPI device
      object matching a given PCI device and we don't choose the one
      that the BIOS expects us to choose, so this makes the lookup take
      more criteria into account in those cases.

   6) Fix to prevent cpuidle from crashing in some rare cases

      If the result of cpuidle_get_driver() is NULL, which can happen on
      some systems, cpuidle_driver_ref() will crash trying to use that
      pointer and the Daniel Fu's fix prevents that from happening.

   7) cpufreq fixes related to CPU hotplug

      Stephen Boyd reported a number of concurrency problems with
      cpufreq related to CPU hotplug which are addressed by a series of
      fixes from Srivatsa S Bhat and Viresh Kumar.

   8) cpufreq fix for time conversion in time_in_state attribute

      Time conversion carried out by cpufreq when user space attempts to
      read /sys/devices/system/cpu/cpu*/cpufreq/stats/time_in_state
      won't work correcty if cputime_t doesn't map directly to jiffies.
      Fix from Andreas Schwab.

   9) Revert of a troublesome cpufreq commit

      Commit 7c30ed5 (cpufreq: make sure frequency transitions are
      serialized) was intended to address some known concurrency
      problems in cpufreq related to the ordering of transitions, but
      unfortunately it introduced several problems of its own, so I
      decided to revert it now and address the original problems later
      in a more robust way.

  10) Intel Haswell CPU models for intel_pstate from Nell Hardcastle.

  11) cpufreq fixes related to system suspend/resume

      The recent cpufreq changes that made it preserve CPU sysfs
      attributes over suspend/resume cycles introduced a possible NULL
      pointer dereference that caused it to crash during the second
      attempt to suspend.  Three commits from Srivatsa S Bhat fix that
      problem and a couple of related issues.

  12) cpufreq locking fix

      cpufreq_policy_restore() should acquire the lock for reading, but
      it acquires it for writing.  Fix from Lan Tianyu"

* tag 'pm+acpi-fixes-3.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (25 commits)
  cpufreq: Acquire the lock in cpufreq_policy_restore() for reading
  cpufreq: Prevent problems in update_policy_cpu() if last_cpu == new_cpu
  cpufreq: Restructure if/else block to avoid unintended behavior
  cpufreq: Fix crash in cpufreq-stats during suspend/resume
  intel_pstate: Add Haswell CPU models
  Revert "cpufreq: make sure frequency transitions are serialized"
  cpufreq: Use signed type for 'ret' variable, to store negative error values
  cpufreq: Remove temporary fix for race between CPU hotplug and sysfs-writes
  cpufreq: Synchronize the cpufreq store_*() routines with CPU hotplug
  cpufreq: Invoke __cpufreq_remove_dev_finish() after releasing cpu_hotplug.lock
  cpufreq: Split __cpufreq_remove_dev() into two parts
  cpufreq: Fix wrong time unit conversion
  cpufreq: serialize calls to __cpufreq_governor()
  cpufreq: don't allow governor limits to be changed when it is disabled
  ACPI / bind: Prefer device objects with _STA to those without it
  ACPI / hotplug / PCI: Avoid parent bus rescans on spurious device checks
  ACPI / hotplug / PCI: Use _OST to notify firmware about notify status
  ACPI / hotplug / PCI: Avoid doing too much for spurious notifies
  ACPICA: Fix for a Store->ArgX when ArgX contains a reference to a field.
  ACPI / hotplug / PCI: Don't trim devices before scanning the namespace
  ...

1  2 
kernel/power/hibernate.c
mm/memory_hotplug.c

diff --combined kernel/power/hibernate.c
index 3085e62a80a5cea43a857cb7b28c50237f71fe39,0b78f72ad39d3ccb3629af5c9d2aafb85ed8a78f..c9c759d5a15cbd53831bdf7552dc455a64a40b5b
@@@ -39,7 -39,7 +39,7 @@@ static int resume_delay
  static char resume_file[256] = CONFIG_PM_STD_PARTITION;
  dev_t swsusp_resume_device;
  sector_t swsusp_resume_block;
 -int in_suspend __nosavedata;
 +__visible int in_suspend __nosavedata;
  
  enum {
        HIBERNATION_INVALID,
@@@ -644,22 -644,23 +644,23 @@@ int hibernate(void
        if (error)
                goto Exit;
  
-       /* Allocate memory management structures */
-       error = create_basic_memory_bitmaps();
-       if (error)
-               goto Exit;
        printk(KERN_INFO "PM: Syncing filesystems ... ");
        sys_sync();
        printk("done.\n");
  
        error = freeze_processes();
        if (error)
-               goto Free_bitmaps;
+               goto Exit;
+       lock_device_hotplug();
+       /* Allocate memory management structures */
+       error = create_basic_memory_bitmaps();
+       if (error)
+               goto Thaw;
  
        error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
        if (error || freezer_test_done)
-               goto Thaw;
+               goto Free_bitmaps;
  
        if (in_suspend) {
                unsigned int flags = 0;
                pr_debug("PM: Image restored successfully.\n");
        }
  
+  Free_bitmaps:
+       free_basic_memory_bitmaps();
   Thaw:
+       unlock_device_hotplug();
        thaw_processes();
  
        /* Don't bother checking whether freezer_test_done is true */
        freezer_test_done = false;
-  Free_bitmaps:
-       free_basic_memory_bitmaps();
   Exit:
        pm_notifier_call_chain(PM_POST_HIBERNATION);
        pm_restore_console();
@@@ -806,21 -807,20 +807,20 @@@ static int software_resume(void
        pm_prepare_console();
        error = pm_notifier_call_chain(PM_RESTORE_PREPARE);
        if (error)
-               goto close_finish;
-       error = create_basic_memory_bitmaps();
-       if (error)
-               goto close_finish;
+               goto Close_Finish;
  
        pr_debug("PM: Preparing processes for restore.\n");
        error = freeze_processes();
-       if (error) {
-               swsusp_close(FMODE_READ);
-               goto Done;
-       }
+       if (error)
+               goto Close_Finish;
  
        pr_debug("PM: Loading hibernation image.\n");
  
+       lock_device_hotplug();
+       error = create_basic_memory_bitmaps();
+       if (error)
+               goto Thaw;
        error = swsusp_read(&flags);
        swsusp_close(FMODE_READ);
        if (!error)
  
        printk(KERN_ERR "PM: Failed to load hibernation image, recovering.\n");
        swsusp_free();
-       thaw_processes();
-  Done:
        free_basic_memory_bitmaps();
+  Thaw:
+       unlock_device_hotplug();
+       thaw_processes();
   Finish:
        pm_notifier_call_chain(PM_POST_RESTORE);
        pm_restore_console();
        mutex_unlock(&pm_mutex);
        pr_debug("PM: Hibernation image not present or could not be loaded.\n");
        return error;
close_finish:
 Close_Finish:
        swsusp_close(FMODE_READ);
        goto Finish;
  }
diff --combined mm/memory_hotplug.c
index 0eb1a1df649d8a149b02eb5989682a5c61e73b3f,53ad1325d7a7632fa0acf6835998a9ec58ed5aad..ed85fe3870e2c5d0094d47c7aa1086084d98235e
@@@ -30,7 -30,6 +30,7 @@@
  #include <linux/mm_inline.h>
  #include <linux/firmware-map.h>
  #include <linux/stop_machine.h>
 +#include <linux/hugetlb.h>
  
  #include <asm/tlbflush.h>
  
@@@ -52,14 -51,10 +52,10 @@@ DEFINE_MUTEX(mem_hotplug_mutex)
  void lock_memory_hotplug(void)
  {
        mutex_lock(&mem_hotplug_mutex);
-       /* for exclusive hibernation if CONFIG_HIBERNATION=y */
-       lock_system_sleep();
  }
  
  void unlock_memory_hotplug(void)
  {
-       unlock_system_sleep();
        mutex_unlock(&mem_hotplug_mutex);
  }
  
@@@ -195,7 -190,7 +191,7 @@@ void register_page_bootmem_info_node(st
  
        zone = &pgdat->node_zones[0];
        for (; zone < pgdat->node_zones + MAX_NR_ZONES - 1; zone++) {
 -              if (zone->wait_table) {
 +              if (zone_is_initialized(zone)) {
                        nr_pages = zone->wait_table_hash_nr_entries
                                * sizeof(wait_queue_head_t);
                        nr_pages = PAGE_ALIGN(nr_pages) >> PAGE_SHIFT;
@@@ -230,8 -225,8 +226,8 @@@ static void grow_zone_span(struct zone 
  
        zone_span_writelock(zone);
  
 -      old_zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages;
 -      if (!zone->spanned_pages || start_pfn < zone->zone_start_pfn)
 +      old_zone_end_pfn = zone_end_pfn(zone);
 +      if (zone_is_empty(zone) || start_pfn < zone->zone_start_pfn)
                zone->zone_start_pfn = start_pfn;
  
        zone->spanned_pages = max(old_zone_end_pfn, end_pfn) -
@@@ -306,7 -301,7 +302,7 @@@ static int __meminit move_pfn_range_lef
                goto out_fail;
  
        /* use start_pfn for z1's start_pfn if z1 is empty */
 -      if (z1->spanned_pages)
 +      if (!zone_is_empty(z1))
                z1_start_pfn = z1->zone_start_pfn;
        else
                z1_start_pfn = start_pfn;
@@@ -348,7 -343,7 +344,7 @@@ static int __meminit move_pfn_range_rig
                goto out_fail;
  
        /* use end_pfn for z2's end_pfn if z2 is empty */
 -      if (z2->spanned_pages)
 +      if (!zone_is_empty(z2))
                z2_end_pfn = zone_end_pfn(z2);
        else
                z2_end_pfn = end_pfn;
@@@ -515,9 -510,8 +511,9 @@@ static int find_biggest_section_pfn(in
  static void shrink_zone_span(struct zone *zone, unsigned long start_pfn,
                             unsigned long end_pfn)
  {
 -      unsigned long zone_start_pfn =  zone->zone_start_pfn;
 -      unsigned long zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages;
 +      unsigned long zone_start_pfn = zone->zone_start_pfn;
 +      unsigned long z = zone_end_pfn(zone); /* zone_end_pfn namespace clash */
 +      unsigned long zone_end_pfn = z;
        unsigned long pfn;
        struct mem_section *ms;
        int nid = zone_to_nid(zone);
        return ret;
  }
  
 +static int check_hotplug_memory_range(u64 start, u64 size)
 +{
 +      u64 start_pfn = start >> PAGE_SHIFT;
 +      u64 nr_pages = size >> PAGE_SHIFT;
 +
 +      /* Memory range must be aligned with section */
 +      if ((start_pfn & ~PAGE_SECTION_MASK) ||
 +          (nr_pages % PAGES_PER_SECTION) || (!nr_pages)) {
 +              pr_err("Section-unaligned hotplug range: start 0x%llx, size 0x%llx\n",
 +                              (unsigned long long)start,
 +                              (unsigned long long)size);
 +              return -EINVAL;
 +      }
 +
 +      return 0;
 +}
 +
  /* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */
  int __ref add_memory(int nid, u64 start, u64 size)
  {
        struct resource *res;
        int ret;
  
 +      ret = check_hotplug_memory_range(start, size);
 +      if (ret)
 +              return ret;
 +
        lock_memory_hotplug();
  
        res = register_memory_resource(start, size);
@@@ -1231,12 -1204,10 +1227,12 @@@ static int test_pages_in_a_zone(unsigne
  }
  
  /*
 - * Scanning pfn is much easier than scanning lru list.
 - * Scan pfn from start to end and Find LRU page.
 + * Scan pfn range [start,end) to find movable/migratable pages (LRU pages
 + * and hugepages). We scan pfn because it's much easier than scanning over
 + * linked list. This function returns the pfn of the first found movable
 + * page if it's found, otherwise 0.
   */
 -static unsigned long scan_lru_pages(unsigned long start, unsigned long end)
 +static unsigned long scan_movable_pages(unsigned long start, unsigned long end)
  {
        unsigned long pfn;
        struct page *page;
                        page = pfn_to_page(pfn);
                        if (PageLRU(page))
                                return pfn;
 +                      if (PageHuge(page)) {
 +                              if (is_hugepage_active(page))
 +                                      return pfn;
 +                              else
 +                                      pfn = round_up(pfn + 1,
 +                                              1 << compound_order(page)) - 1;
 +                      }
                }
        }
        return 0;
@@@ -1272,19 -1236,6 +1268,19 @@@ do_migrate_range(unsigned long start_pf
                if (!pfn_valid(pfn))
                        continue;
                page = pfn_to_page(pfn);
 +
 +              if (PageHuge(page)) {
 +                      struct page *head = compound_head(page);
 +                      pfn = page_to_pfn(head) + (1<<compound_order(head)) - 1;
 +                      if (compound_order(head) > PFN_SECTION_SHIFT) {
 +                              ret = -EBUSY;
 +                              break;
 +                      }
 +                      if (isolate_huge_page(page, &source))
 +                              move_pages -= 1 << compound_order(head);
 +                      continue;
 +              }
 +
                if (!get_page_unless_zero(page))
                        continue;
                /*
        }
        if (!list_empty(&source)) {
                if (not_managed) {
 -                      putback_lru_pages(&source);
 +                      putback_movable_pages(&source);
                        goto out;
                }
  
                ret = migrate_pages(&source, alloc_migrate_target, 0,
                                        MIGRATE_SYNC, MR_MEMORY_HOTPLUG);
                if (ret)
 -                      putback_lru_pages(&source);
 +                      putback_movable_pages(&source);
        }
  out:
        return ret;
@@@ -1517,6 -1468,7 +1513,6 @@@ static int __ref __offline_pages(unsign
        struct zone *zone;
        struct memory_notify arg;
  
 -      BUG_ON(start_pfn >= end_pfn);
        /* at least, alignment against pageblock is necessary */
        if (!IS_ALIGNED(start_pfn, pageblock_nr_pages))
                return -EINVAL;
@@@ -1571,8 -1523,8 +1567,8 @@@ repeat
                drain_all_pages();
        }
  
 -      pfn = scan_lru_pages(start_pfn, end_pfn);
 -      if (pfn) { /* We have page on LRU */
 +      pfn = scan_movable_pages(start_pfn, end_pfn);
 +      if (pfn) { /* We have movable pages */
                ret = do_migrate_range(pfn, end_pfn);
                if (!ret) {
                        drain = 1;
        yield();
        /* drain pcp pages, this is synchronous. */
        drain_all_pages();
 +      /*
 +       * dissolve free hugepages in the memory block before doing offlining
 +       * actually in order to make hugetlbfs's object counting consistent.
 +       */
 +      dissolve_free_huge_pages(start_pfn, end_pfn);
        /* check again */
        offlined_pages = check_pages_isolated(start_pfn, end_pfn);
        if (offlined_pages < 0) {
@@@ -1723,8 -1670,9 +1719,8 @@@ static int is_memblock_offlined_cb(stru
        return ret;
  }
  
 -static int check_cpu_on_node(void *data)
 +static int check_cpu_on_node(pg_data_t *pgdat)
  {
 -      struct pglist_data *pgdat = data;
        int cpu;
  
        for_each_present_cpu(cpu) {
        return 0;
  }
  
 -static void unmap_cpu_on_node(void *data)
 +static void unmap_cpu_on_node(pg_data_t *pgdat)
  {
  #ifdef CONFIG_ACPI_NUMA
 -      struct pglist_data *pgdat = data;
        int cpu;
  
        for_each_possible_cpu(cpu)
  #endif
  }
  
 -static int check_and_unmap_cpu_on_node(void *data)
 +static int check_and_unmap_cpu_on_node(pg_data_t *pgdat)
  {
 -      int ret = check_cpu_on_node(data);
 +      int ret;
  
 +      ret = check_cpu_on_node(pgdat);
        if (ret)
                return ret;
  
         * the cpu_to_node() now.
         */
  
 -      unmap_cpu_on_node(data);
 +      unmap_cpu_on_node(pgdat);
        return 0;
  }
  
 -/* offline the node if all memory sections of this node are removed */
 +/**
 + * try_offline_node
 + *
 + * Offline a node if all memory sections and cpus of the node are removed.
 + *
 + * NOTE: The caller must call lock_device_hotplug() to serialize hotplug
 + * and online/offline operations before this call.
 + */
  void try_offline_node(int nid)
  {
        pg_data_t *pgdat = NODE_DATA(nid);
                return;
        }
  
 -      if (stop_machine(check_and_unmap_cpu_on_node, pgdat, NULL))
 +      if (check_and_unmap_cpu_on_node(pgdat))
                return;
  
        /*
  }
  EXPORT_SYMBOL(try_offline_node);
  
 +/**
 + * remove_memory
 + *
 + * NOTE: The caller must call lock_device_hotplug() to serialize hotplug
 + * and online/offline operations before this call, as required by
 + * try_offline_node().
 + */
  void __ref remove_memory(int nid, u64 start, u64 size)
  {
        int ret;
  
 +      BUG_ON(check_hotplug_memory_range(start, size));
 +
        lock_memory_hotplug();
  
        /*