|
@@ -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;
|