summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/node.c57
-rw-r--r--include/linux/node.h5
2 files changed, 52 insertions, 10 deletions
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 9e218a6d4a5b..54e5d8eaf70e 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -186,11 +186,14 @@ static SYSDEV_ATTR(distance, S_IRUGO, node_read_distance, NULL);
static node_registration_func_t __hugetlb_register_node;
static node_registration_func_t __hugetlb_unregister_node;
-static inline void hugetlb_register_node(struct node *node)
+static inline bool hugetlb_register_node(struct node *node)
{
if (__hugetlb_register_node &&
- node_state(node->sysdev.id, N_HIGH_MEMORY))
+ node_state(node->sysdev.id, N_HIGH_MEMORY)) {
__hugetlb_register_node(node);
+ return true;
+ }
+ return false;
}
static inline void hugetlb_unregister_node(struct node *node)
@@ -387,10 +390,31 @@ static int link_mem_sections(int nid)
return err;
}
+#ifdef CONFIG_HUGETLBFS
/*
* Handle per node hstate attribute [un]registration on transistions
* to/from memoryless state.
*/
+static void node_hugetlb_work(struct work_struct *work)
+{
+ struct node *node = container_of(work, struct node, node_work);
+
+ /*
+ * We only get here when a node transitions to/from memoryless state.
+ * We can detect which transition occurred by examining whether the
+ * node has memory now. hugetlb_register_node() already check this
+ * so we try to register the attributes. If that fails, then the
+ * node has transitioned to memoryless, try to unregister the
+ * attributes.
+ */
+ if (!hugetlb_register_node(node))
+ hugetlb_unregister_node(node);
+}
+
+static void init_node_hugetlb_work(int nid)
+{
+ INIT_WORK(&node_devices[nid].node_work, node_hugetlb_work);
+}
static int node_memory_callback(struct notifier_block *self,
unsigned long action, void *arg)
@@ -399,14 +423,16 @@ static int node_memory_callback(struct notifier_block *self,
int nid = mnb->status_change_nid;
switch (action) {
- case MEM_ONLINE: /* memory successfully brought online */
+ case MEM_ONLINE:
+ case MEM_OFFLINE:
+ /*
+ * offload per node hstate [un]registration to a work thread
+ * when transitioning to/from memoryless state.
+ */
if (nid != NUMA_NO_NODE)
- hugetlb_register_node(&node_devices[nid]);
- break;
- case MEM_OFFLINE: /* or offline */
- if (nid != NUMA_NO_NODE)
- hugetlb_unregister_node(&node_devices[nid]);
+ schedule_work(&node_devices[nid].node_work);
break;
+
case MEM_GOING_ONLINE:
case MEM_GOING_OFFLINE:
case MEM_CANCEL_ONLINE:
@@ -417,15 +443,23 @@ static int node_memory_callback(struct notifier_block *self,
return NOTIFY_OK;
}
-#else
+#endif /* CONFIG_HUGETLBFS */
+#else /* !CONFIG_MEMORY_HOTPLUG_SPARSE */
+
static int link_mem_sections(int nid) { return 0; }
+#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
+#if !defined(CONFIG_MEMORY_HOTPLUG_SPARSE) || \
+ !defined(CONFIG_HUGETLBFS)
static inline int node_memory_callback(struct notifier_block *self,
unsigned long action, void *arg)
{
return NOTIFY_OK;
}
-#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
+
+static void init_node_hugetlb_work(int nid) { }
+
+#endif
int register_one_node(int nid)
{
@@ -449,6 +483,9 @@ int register_one_node(int nid)
/* link memory sections under this node */
error = link_mem_sections(nid);
+
+ /* initialize work queue for memory hot plug */
+ init_node_hugetlb_work(nid);
}
return error;
diff --git a/include/linux/node.h b/include/linux/node.h
index dae1521e1f05..06292dac3eab 100644
--- a/include/linux/node.h
+++ b/include/linux/node.h
@@ -21,9 +21,14 @@
#include <linux/sysdev.h>
#include <linux/cpumask.h>
+#include <linux/workqueue.h>
struct node {
struct sys_device sysdev;
+
+#if defined(CONFIG_MEMORY_HOTPLUG_SPARSE) && defined(CONFIG_HUGETLBFS)
+ struct work_struct node_work;
+#endif
};
struct memory_block;