|
@@ -164,13 +164,65 @@ int online_pages(unsigned long pfn, unsigned long nr_pages)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static pg_data_t *hotadd_new_pgdat(int nid, u64 start)
|
|
|
+{
|
|
|
+ struct pglist_data *pgdat;
|
|
|
+ unsigned long zones_size[MAX_NR_ZONES] = {0};
|
|
|
+ unsigned long zholes_size[MAX_NR_ZONES] = {0};
|
|
|
+ unsigned long start_pfn = start >> PAGE_SHIFT;
|
|
|
+
|
|
|
+ pgdat = arch_alloc_nodedata(nid);
|
|
|
+ if (!pgdat)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ arch_refresh_nodedata(nid, pgdat);
|
|
|
+
|
|
|
+ /* we can use NODE_DATA(nid) from here */
|
|
|
+
|
|
|
+ /* init node's zones as empty zones, we don't have any present pages.*/
|
|
|
+ free_area_init_node(nid, pgdat, zones_size, start_pfn, zholes_size);
|
|
|
+
|
|
|
+ return pgdat;
|
|
|
+}
|
|
|
+
|
|
|
+static void rollback_node_hotadd(int nid, pg_data_t *pgdat)
|
|
|
+{
|
|
|
+ arch_refresh_nodedata(nid, NULL);
|
|
|
+ arch_free_nodedata(pgdat);
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
int add_memory(int nid, u64 start, u64 size)
|
|
|
{
|
|
|
+ pg_data_t *pgdat = NULL;
|
|
|
+ int new_pgdat = 0;
|
|
|
int ret;
|
|
|
|
|
|
+ if (!node_online(nid)) {
|
|
|
+ pgdat = hotadd_new_pgdat(nid, start);
|
|
|
+ if (!pgdat)
|
|
|
+ return -ENOMEM;
|
|
|
+ new_pgdat = 1;
|
|
|
+ ret = kswapd_run(nid);
|
|
|
+ if (ret)
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
/* call arch's memory hotadd */
|
|
|
ret = arch_add_memory(nid, start, size);
|
|
|
|
|
|
+ if (ret < 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ /* we online node here. we have no error path from here. */
|
|
|
+ node_set_online(nid);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+error:
|
|
|
+ /* rollback pgdat allocation and others */
|
|
|
+ if (new_pgdat)
|
|
|
+ rollback_node_hotadd(nid, pgdat);
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(add_memory);
|